Die Klasse ESP8266WebServer ist eher weniger gut dokumentiert. Hier einige Dinge, die im Laufe der Zeit klarer geworden sind.

In­halts­ver­zeich­nis

1.  HTTP

1.1  HTTP-Adressierung

1.2  HTTP-Request

2.  ESP8266WebServer-Klasse

2.1  Funktionsprinzip

2.2  Empfang und Analyse des HTTP-Request

2.2.1  HTTP-Method und Dateipfad

2.2.2  Parameter-Elemente

2.2.3  Header-Elemente

2.3  Initialisierung

2.3.1  Konstruktor / Destruktor

2.3.2  Handler registrieren

2.3.3  Webserver starten / stoppen

2.4  Daten senden

2.5  Authentifizierung

3.  Formulare

3.1 form-inp

3.1.1  Plain post json oder andere Daten

3.2  Datei-Upload

3.2.1  Prozess

4.  Minimal-Implementierung

5.  Download


Hinweis: Erweiterte Debugging-Ausgaben über die serielle Schnittstelle erhält man, wenn man die Konstante DEBUG_ESP_HTTP_SERVER definiert.

HTTP

Zunächst einige zentrale Begriffe zu HTTP.

Eine ausführliche Behandlung findet man bei CodeProjekt: The HTTP series von Vladimir Pecanac.

HTTP-Adressierung

Zur Identifizierung der zurückzuliefernden Daten wird vom HTTP-Client ein URL (Uniform Resource Locator) im HTTP-Header an den HTTP-Server übermittelt. Eine URL hat prinzipiell den folgenden Aufbau (ohne Leerzeichen!):

<Ressourcentyp> :// <User> : <Passwort> @ <Host.Domain.TLD> : <Port> / <Pfad/Datei>    ? Parametername = Parameterwert & Parametername = Parameterwert

http          :// anonym : My$pw    @ bienonline.magix.net : 80    / public/index.html  ? search = ATmega & datesince = 2016-01-01

Einige der Angaben sind optional.

HTTP-Request

Ein HTTP-Request ist ein Text-Element und hat prinzipiell folgenden Aufbau (elektronik-kompendium, w3.org):

<Method> <URL> HTTP/<Version> <CRLF>
<HeaderName>: <HeaderValue> <CRLF>
<HeaderName>: <HeaderValue> <CRLF>
<CRLF>  //Leerzeile!
<MessageBody>
...

GET /public/esp8266-webserver-klasse.html HTTP/1.1↵
Host: bienonline.magix.net↵
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729)↵
...

Die URL selbst besteht aus einer Pfadangabe zu der gewünschten Datei und einem optionalen Query-String:

<Path> ? <ParameterName> = <ParameterValue> [ & <ParameterName> = <ParameterValue> ... ]

www.example.org/suche?stichwort=wiki&ausgabe=liste

ESP8266WebServer-Klasse

Hinweis: Die folgenden Angaben beziehen sich auf die Version 2.3.0 des Arduino core for ESP8266 WiFi chip.

ESP8266WebServer ist i.W. ein Wrapper um die Klasse WiFiServer. WiFiServer ist ein allgemeiner TCP-Server, ESP8266WebServer behandelt die Besonderheiten des HTTP-Protokolls.

Funktionsprinzip

Nach der Initialisierung einer Instanz der Klasse (ctor, begin()) muss das Programm dafür sorgen, dass regelmäßig überprüft wird, ob ein neuer HTTP-Request eingetroffen ist (handleClient()). Ein erhaltener Request wird analysiert und in seine Bestandteile zerlegt. Diese Elemente werden über entsprechende Methoden veröffentlicht (z.B. uri()).

Anhand der HTTP-Methode (z.B. GET) und der angeforderten Datei (z.B. "/config.html") wird eine vorher registrierte (on()) Bearbeitungsmethode (Callback, Handler) ermittelt und aufgerufen. Diese stellt den Seitentext zusammen und sendet ihn zum Anforderer zurück (send()). Wurde kein entsprechender Handler registriert, wird die registrierte Standard-Bearbeitungsmethode (onNotFound()) aufgerufen. Wurde auch diese nicht definiert, wird eine Fehlerseite zurückgeliefert (404, Not found).

Sequence-Diagramm

Neben den Handlern für die Seitenbearbeitung gibt es einen, der einen angeforderten Datei-Upload ausführt.

Empfang und Analyse des HTTP-Request

HTTP-Method und Dateipfad

Für die Entgegennahme von HTTP-Anfragen ist die Methode handleClient() zuständig. Hier wird nachgeschaut, ob ein neuer HTTP-Request vorliegt und dieser anschließend analysiert. Aus diesem Grund muss handleClient() regelmäßig (loop()) aufgerufen werden.

 Die eigentliche Analyse wird von der geschützten Methode _parseRequest() erledigt. Folgende Felder werden gefüllt:

HTTPMethod  _currentMethod;
String      _currentUri;
RequestHandler* _currentHandler;

_currentHandler wird anhand von _currentMethod und _currentUri in der Handler-Liste (s. Methode on()) gesucht.

Außerdem wird die Parameter-Liste aufgebaut (s.u.) und die angeforderten Header-Elemente (s.u.) extrahiert.

Die ermittelten Daten sind über öffentliche Methoden abrufbar:

Methode Funktion
String uri() Ruft den (nackten) Pfad auf die angeforderte Datei ab.
HTTPMethod method() Ruft den Typ des HTTP-Requests ab. Mögliche Werte sind:
HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS

Parameter-Elemente

Auf in der URL hinterlegte Parameter (Query-String) kann über den folgenden Methoden-Komplex zugegriffen werden:

Methode Funktion
int args() Ruft die Anzahl der gespeicherten Parameter ab.
String arg(int i) Ruft den Parameter-Wert mit dem nullbasierten Index i ab.
Liefert einen leeren String, wenn der Index außerhalb des definierten Bereichs liegt.
String argName(int i) Ruft den Parameter-Namen mit dem nullbasierten Index i ab.
Liefert einen leeren String, wenn der Index außerhalb des definierten Bereichs liegt.
String arg(String name) Ruft den Paramter-Wert mit dem Namen name ab.
Liefert einen leeren String, wenn der ein Header mit dem angegeben Namen nicht gespeichert wurde.
bool hasArg(String name) Prüft, ob der Paramter mit den Namen name existiert.
Serial.print("Paramter: ");
Serial.println(webserver.args());

for (int i = 0; i < webserver.args(); i++)
  Serial.printf("Paramter[%i]: %s: %s\n", i, webserver.argName(i).c_str(), webserver.arg(i).c_str());
http://192.168.xxx.xxx/?m=1&q=33
Paramter: 2
Paramter[0]: m: 1
Paramter[1]: q: 33

Die statische Methode urlDecode() tauscht Ersatzzeichen im übergegeben String gegen reguläre Zeichen aus. Parameterwerte werden während der Analyse dekodiert. Die HTTP-Adresse

https://translate.google.de/translate?hl=de&sl=en&u=http://www.esp8266.com/viewtopic.php%3Fp%3D48622&prev=search

 enthält den Parameter u mit dem Wert http://www.esp8266.com/viewtopic.php%3Fp%3D48622. %3F und %3D werden ersetzt durch ? und =. Der Methodenaufruf arg("u") liefert dann:
     http://www.esp8266.com/viewtopic.php?p=48622

Header-Elemente

Um Zugriff auf die Header zu erhalten, muss vorab festgelegt werden, welche Header-Werte gespeichert werden sollen. Diese geschieht über die Methode collectHeaders(). Dieser Methode wird eine Liste der gewünschten Namen übergeben:

// Gewünschte Header-Elemente festlegen
const char* Headers[] = {"User-Agent", "Connection"};

...

// Speicherung anfordern
webserver.collectHeaders(Headers, sizeof(Headers)/ sizeof(Headers[0]));

...

// Gespeicherte Header-Elemente ausgeben
Serial.print("Headers: ");
Serial.println(webserver.headers());

for (int i = 0; i < webserver.headers(); i++)
  Serial.printf("Header[%i]: %s\n", i, webserver.header(i).c_str());
Headers: 3
Header[0]:
Header[1]: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0
Header[2]: keep-alive

Der Zugriff auf die Daten erfolgt durch den Methodenkomplex:

Methode Funktion
int headers() Ruft die Anzahl der gespeicherten Header ab.
String header(int i) Ruft den Header-Wert mit dem nullbasierten Index i ab.
Liefert einen leeren String, wenn der Index außerhalb des definierten Bereichs liegt.
String headerName(int i) Ruft den Header-Namen mit dem nullbasierten Index i ab.
Liefert einen leeren String, wenn der Index außerhalb des definierten Bereichs liegt.
String header(String name) Ruft den Header-Wert mit dem Namen name ab.
Liefert einen leeren String, wenn der ein Header mit dem angegeben Namen nicht gespeichert wurde.
bool hasHeader(String name) Prüft, ob der Header mit den Namen name existiert.

Der Header "Host" wird unabhängig von dieser Liste in dem geschützten Feld _ostHeader gesichert und über die öffentliche Methode hostHeader() zur Verfügung gestellt.

Initialisierung

Um den oben beschriebenen Funktionsumfang ausnutzen zu können, muss die ESP8266WebServer-Klasse entsprechend initialisiert werden.

Konstruktor / Destruktor

Methode Funktion
ESP8266WebServer(int port = 80) Initialisiert eine neue Instanz der ESP8266WebServer-Klasse. Der Standard-Port ist 80.
Diese Version des Konstruktors sollte verwandt werden, nur eine einzelne Netzwerkverbindung besteht. Der Webserver wird dann an diese gebunden.
ESP8266WebServer(IPAddress addr, int port = 80) Initialisiert eine neue Instanz der ESP8266WebServer-Klasse und bindet sie an die angegebene IP-Adresse. Der Standard-Port ist 80.
Diese Version des Konstruktors muss verwandt werden, wenn mehr als eine einzelne Netzwerkverbindung besteht.
~ESP8266WebServer() Gibt die der Instanz zugewiesenen Ressourcen wieder frei.

Handler registrieren

Zur Bearbeitung eingehender HTTP-Request müssen entsprechende Methoden kodiert werden. Die Methoden sind wie folgt deklariert:

typedef std::function<void(void)> THandlerFunction;

Also z.B. void handleIndex(void) zur Bearbeitung einer Requests auf die Datei "index.html". Die hinterlegten Methoden müssen der ESP8266WebServer-Instanz bekannt gemacht werden:

Methode Funktion
void on(const char* uri, THandlerFunction handler) Erstellt einen neue Instanz der FunctionRequestHandler-Klasse und fügt sie der Handler-Liste hinzu.
fn wird für eine beliebige HTTP-Method aufgerufen, wenn ein HTTP-Request mit der angegebenen URI eintrifft. Als Upload-Handler wird der durch onFileUpload() hinterlegte Handler genutzt (s. Formulare).
void on(const char* uri, HTTPMethod method, THandlerFunction fn) Erstellt einen neue Instanz der FunctionRequestHandler-Klasse und fügt sie der Handler-Liste hinzu.
fn wird aufgerufen, wenn ein HTTP-Request mit der angegebenen URI und der angegeben Methode eintrifft. Als Upload-Handler wird der durch onFileUpload() hinterlegte Handler genutzt (s. Formulare).
void on(const char* uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn) Erstellt einen neue Instanz der FunctionRequestHandler-Klasse und fügt sie der Handler-Liste hinzu.
fn wird aufgerufen, wenn ein HTTP-Request mit der angegebenen URI und der angegeben Methode eintrifft. Als Upload-Handler wird der in ufn  hinterlegte Handler genutzt (s. Formulare).
void onNotFound(THandlerFunction fn) fn wird aufgerufen, wenn keine andere Methode für URI/Method-Kombination des Request hinterlegt ist.
void onFileUpload(THandlerFunction fn)  fn wird in der geschützten Variablen _fileUploadHandler abgelegt. Diese hinterlegte Funktion wird bei den Upload-Requests aufgerufen, für die kein eigener Upload-Handler angegeben wurde.
void addHandler(RequestHandler* handler) Fügt die angegebene Instanz der RequestHandler-Klasse der Handler-Liste hinzu.
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ) Definiert einen Handler zum Upload einer Datei die mit der HTTP-Method GET angefordert werden.
uri: URI für den dieser Handler gelten soll
fs: Dateisystem aus dem die Datei geladen werden soll.
path: Absoluter Pfad zur Datei
Bespiel: serveStatic("/favicon.png", SPIFFS, "/espressif.png")
Wenn die Datei "/favicon.png" angefordert wird, wird die Datei "/espressif.png" aus dem Dateisystem SPIFFS zurückgeliefert.

Zeigt path auf eine Directory, wird versucht, die Datei "<path>/index.html" zu laden.

Im vierten Parameter können Angaben zum Cache-Control gemacht werden, d.h. die Angabe wie lange die Datei im Browser-Cache gehalten werden soll. Also cache_header := "max-age=2000". Die Angabe erfolgt in Sekunden.

Webserver starten / stoppen

Die Methode begin() bindet den WebServer an TCP, d.h. TCP-Pakete an den im WebServer festgelegten Port werden an den WebServer weitergegeben.

close() oder gleichwertig stop() heben die Bindung wieder auf.

Daten senden

Um Daten an den Client zurück zu senden (HTTP-Response) wird die Methode send() genutzt. Die drei Parameter haben folgende Bedeutung:

Parameter Bedeutung
int code HTTP response code,  200 oder 404
char* content_type HTTP content type, wie "text/plain" oder "image/png"
String& content Der content

send() steht in verschiedenen Varianten zur Verfügung.

Mit

kann der Response auch einzeln aufgebaut werden.

Authentifizierung

Authentifizierungsdaten (Account-Name und Passwort) werden über einen speziellen Header übertragen. Name und Passwort sind verschlüsselt:

Authorization: Basic dWxsaToxMjM=

Für die Authentifizierung stehen zwei Methoden zur Verfügung:

Methode Funktion
bool authenticate(const char * username, const char * password) Fragt ab, ob Authentifizierungsdaten für das angegebene Konto vorliegt.
void requestAuthentication() Fordert Authentifizierungsdaten vom Browser an.

Eine typische Kodierung ist:

if (!webserver.authenticate(User, Password))
  return webserver.requestAuthentication();

Sind mehrere Accounts für den Zugriff auf die Seite zugelassen, ist die Methode authenticate() mehrfach aufzurufen.

Formulare

Die Gestaltung von Formularen ist bei selfhtml beschrieben. Wesentlich sind Formular-Elemente zur Eingabe von Daten (input, textarea) und button-Elemente zum Auslösen von Aktionen.

Dateneingabe und -übermittlung

Das Klicken auf Formular-Elemente mit dem type-Attribut "submit" führen zur Übertragung der Formular-Daten der Felder mit den Tags <input> oder <textarea> an den Server. Die eingegebenen Daten werden im MessageBody übertagen. Das Format ist <name>=<value><crlf><name>=<value><crlf>...

Eine HTML-Seite "test.html" mit dem folgenden form-Element:

<form action="site.html" method="POST">
   <input name="ButtonName" type="submit" value="ButtonValue">
</form>

führt im Browser zur Anzeige der Schaltfläche

Wird diese gedrückt, wird die Seite "site.html" (= action-Attribut) mit folgendem Request angefordert:

POST /site.html HTTP/1.1
Host: 192.168.xxx.xxx
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://192.168.xxx.xxx/test.html
Authorization: Basic dWxsaToxMjM=
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 22

ButtonName=ButtonValue   <= MessageBody

MessageBody erhält den Inhalt "ButtonName=ButtonValue". Dies entspricht den Angaben bei name und value des input-Elements.

Die ESP8266WebServer-Klasse hängt diese Daten an den Query-String an, erzeugt also ein neues Parameter-Element (s.o.). Die Methode arg("ButtonName") würde also den Wert "ButtonValue" liefern.

Interner Code:

searchStr += plainBuf; // searchStr := QueryString, MessageBody := plainBuf

Plain post json oder andere Daten

Beginnt der MessageBody  mit "{", "[" oder enthält kein "=" ( wie sieht das HTML dazu aus), wird das Parameter-Element mit den Namen "plain" erzeugt.

Interner Code:

// searchStr := QueryString, MessageBody := plainBuf

if(plainBuf[0] == '{' || plainBuf[0] == '[' || strstr(plainBuf, "=") == NULL)
{ //plain post json or other data
  searchStr += "plain=";
  searchStr += plainBuf;
...

Datei-Upload

Wie ein HTML-form-Element gestaltet sein muss, um eine Datei hoch zu laden, ist bei selfhtml beschrieben. Das form-Element muss das enctype="multipart/form-data"-Attribut besitzen. Ein input-Element mit dem type-Attribut "file" dient zur Auswahl der Datei, eine Schaltfläche zum hochladen.

<form action="site.html" enctype="multipart/form-data" method="POST">
   Datei hochladen 
   <input name="datei" size="50" type="file"> &nbsp;
   <button>Hochladen</button>
</form>

Die ESP8266WebServer-Klasse erkennt, dass eine Datei hochgeladen werden soll und und ruft den zur uri und HTTP-Method  registrierten Upload-Handler auf. Die Datei-Informationen werden über die Methode HTTPUpload& upload() in Form einer Referenz auf eine HTTPUpload-Instanz zur Verfügung gestellt.

typedef struct {
  HTTPUploadStatus status;
  String  filename;
  String  name;         // Dateiname (?) bei Content-Disposition
  String  type;         // Content type ("text/plain", ...)
  size_t  totalSize;    // File Size
  size_t  currentSize;  // Size of data currently in buf
  uint8_t buf[HTTP_UPLOAD_BUFLEN];
} HTTPUpload;

Prozess

Die registrierte Upload-Methode wird ggf. mehrfach aufgerufen, bis der komplette Dateiinhalt übertragen wurde. Die jeweils auszuführen Aktion kann anhand von HTTPUpload::status ermittelt werden:

Status Aktion
UPLOAD_FILE_START Datei öffnen
UPLOAD_FILE_WRITE Datei schreiben
UPLOAD_FILE_END Datei schließen
UPLOAD_FILE_ABORTED Datei schließen und löschen

Ein Standard-Upload-Handler könnte wie folgt aussehen:

void handleUpload()
{ static File fsUploadFile;                // File-Handle zur Datei
  HTTPUpload& upload = webserver.upload(); // Upload-Daten
  String UploadFileName;                   // Datei-Name

  switch (upload.status)
  { case UPLOAD_FILE_START:
      UploadFileName = upload.filename;
      if (!UploadFileName.startsWith("/"))
        UploadFileName = "/" + UploadFileName;
      fsUploadFile = SPIFFS.open(UploadFileName, "w");
      break;

    case UPLOAD_FILE_WRITE:
      if (!fsUploadFile)
        break;

      fsUploadFile.write(upload.buf, upload.currentSize);
      break;

    case UPLOAD_FILE_END:
      if (!fsUploadFile)
        break;

      fsUploadFile.close();
      break;

    case UPLOAD_FILE_ABORTED:
      if (!fsUploadFile)
        break;

      fsUploadFile.close();
      SPIFFS.remove(UploadFileName);
      break;

  default:
    break;
  }
}

Minimal-Implementierung

Die folgende Applikation verbindet sich mit einem WLAN, versucht zu HTTP-Anfragen passende Dateien aus dem SPIFFS zurück zu liefern. Das Filesystem enthält die Dateien index.html, ulli.html, espressif.png.

Die einzige Besonderheit im Code ist die Zeile

    webserver.serveStatic("/", SPIFFS, "/index.html");

 Die Eingabe der reinen IP-Adresse wird auf '/index.html' umgelenkt.

Index.html

<!DOCTYPE HTML>
<html lang="de">

<head>
   <title>ESP8266-MinWebServer</title>
   <link href="espressif.png" rel="shortcut icon" type="image/x-icon">
   <meta charset="UTF-8"> 
 </head>

<body>
<h1>ESP8266-MinWebServer</h1>
<p>Hier geht's zur <a href="ulli.html">zweiten Seite</a></p>
</body>
</html>

ulli.html

<!DOCTYPE HTML>
<html lang="de">

<head>
   <meta content="de" http-equiv="Content-Language">
   <title>ESP8266-MinWebServer</title>
   <link href="espressif.png" rel="shortcut icon" type="image/x-icon">
   <meta charset="UTF-8"> 
 </head>

<body>

<p style="font-family: Arial, Helvetica, sans-serif">Dokumentation zu diesem Projekt findet man auf 
der Seite <a href="http://bienonline.magix.net/public/esp8266-webserver-klasse.html">ESP8266 
Webserver</a> in</p>
<h1 style="font-family: Arial, Helvetica, sans-serif"><a href="http://ullisroboterseite.de">Ullis 
Roboter Seite</a></h1>

</body>

</html>

ESP8266-Code:

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h> // muss vor <detail\RequestHandlersImpl.h> stehen (s. Hinweis im Anschluss)
#include <detail\RequestHandlersImpl.h>

String ssid("your ssid");
String password("your password");

ESP8266WebServer webserver(80); // Webserver-Instanz für Port 80 erstellen

void notFound();      // Sendet "Not Found"-Seite
void handleUnknown(); // Liefert Web-Seiten aus dem SPIFFS

void setup()
{ Serial.begin(74880);

  Serial.println("\n");
  Serial.flush();
  delay(100);
  Serial.println("                            ");
  Serial.println("----------------------------");
  Serial.println("Project: ESP8266-MinWebServer");

  // Initialize file system.
  if (!SPIFFS.begin())
  { Serial.println("SPIFFS nicht initialisiert!");
    while (1)  // ohne SPIFFS geht es sowieso nicht...
    { yield();
    }
  }
  Serial.println("SPIFFS ok");

  // Mit WLAN verbinden
  Serial.println("Connecting to " + ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid.c_str(), password.c_str());
  while (WiFi.waitForConnectResult() != WL_CONNECTED)
  { Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }
  Serial.print("Connected to " + ssid + ", IP: " );
  Serial.println(WiFi.localIP());

  // Webserver initilaisieren
  // Lenkt die Abfrage unbekanter Dateien auf handleUnknown
  webserver.onNotFound(handleUnknown); 
  // Die Abfrage auf die reine URL '/' wird auf '/index.html' umgelenkt
  webserver.serveStatic("/", SPIFFS, "/index.html");

  webserver.begin(); // Web-Server starten
  Serial.println("HTTP Server running on Port 80");
}

void loop()
{ webserver.handleClient(); // auf neuen HTTP-Request prüfen
}


// Sendet "Not Found"-Seite
void notFound()
{ String HTML = F("<html><head><title>404 Not Found</title></head><body>"
                  "<h1>Not Found</h1>"
                  "<p>The requested URL was not found on this webserver.</p>"
                  "</body></html>");
  webserver.send(404, "text/html", HTML);
}

// Es wird versucht, die angegebene Datei aus dem SPIFFS hochzuladen
void handleUnknown()
{ String filename = webserver.uri();

  File pageFile = SPIFFS.open(filename, "r");
  if (pageFile)
  { String contentTyp = StaticRequestHandler::getContentType(filename);
    size_t sent = webserver.streamFile(pageFile, contentTyp);
    pageFile.close();
  }
  else
    notFound();
}

Der folgende Code-Teil bedarf noch einer Erläuterung:

#include // muss vor <detail\RequestHandlersImpl.h> stehen
#include <detail\RequestHandlersImpl.h>

In der Methode handleUnknown() wird versucht, eine HTTP-Anfrage, für die kein separater Handler definiert ist, über die Rückgabe einer Datei aus dem SPIFFS zu befriedigen. Bei send(..) muss man einen “Content-Type” mitgeben, z.B. “text/plain”. In dem Bespiel zu ESP8266WebServer-Klasse gibt es eine eigene Passage, die aus einer Datei-Endung den Content-Type ableitet:

if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf("."));
else if(path.endsWith(".htm")) dataType = "text/html";
else if(path.endsWith(".css")) dataType = "text/css";
else if(path.endsWith(".js")) dataType = "application/javascript";
else if(path.endsWith(".png")) dataType = "image/png";
...
In der ESP8266WebServer-Bibliothek gibt es aber bereits eine Methode, die genau dies macht: StaticRequestHandler::getContentType(..). Diese Methode wird in “detail\RequestHandlersImpl.h” definiert. Diese Include-Datei benötigt aber Definitionen aus “FS.h”. “ESP8266WebServer.cpp” bindet beide Dateien ein, und zwar “FS.h” vor “detail\RequestHandlersImpl.h”. Will man nun Methoden aus “detail\RequestHandlersImpl.h” nutzen, muss man vor dem Einbinden dieser Datei immer “FS.h” einbinden.
 
Das ist: schlechter Programmierstil in der Bibliothek! Eine Datei sollte alle notwendigen Includes selbst enthalten und nicht auf die Reihenfolge der Einbindung oder andere Dateien angewiesen sein. Noch besser wäre es gewesen, getContentType(..) gleich in der ESP8266WebServer-Klasse zu hinterlegen. Denn dies ist eine nützliche Funktion, die öffentlich bereit gestellt werden sollte.

Download

Download Projekt

Download Projekt