Webserver Verständnisfrage

Hallo Zusammen,

ich verwende den Beispielsketch von Renzo Mischianti (unten angehängt), um auf einem ESP32 einen Webserver zu realisieren, der seine index.html Seite von einer SD Karte holt. Das Beispiel verwendet SPIFFS aber ich habe das auf SD Karte geändert.
Der Sketch funktioniert, allerdings verstehe ich eine Sache nicht und da hoffe ich, ihr könnt Licht ins Dunkel bringen:

Soweit ich verstanden habe, wird normalerweise im setup() mit server.on(callback) dem Server mitgeteilt, welche Callback Funktion ausgeführt werden soll, wenn ein Client Request reinkommt, richtig?
Also zum Bsp. die Funktion handleRoot(), die dann die index.htm Seite sendet, wie in diesem Bsp.: A Beginner's Guide to the ESP8266.
Wenn im laufenden Betrieb also ein Client request reinkommt, wird dieser in loop() über server.handleClient erkannt und dann wird im o.g. Beispiel die Funktion handleRoot() aufgerufen, die dann index.htm an den Client sendet.

Jetzt kommt mein Problem:
In dem Sketch von Renzo gibt es kein server.on(callback).
Es wird in setup() die Funktion serverRouting() aufgerufen, die dann handleFileRead() aufruft, die ihrerseits dann die index.htm sendet.
Woher weiß im laufenden Betrieb, wenn sich der Programmablauf nur noch in loop() abspielt, die Methode server.handleClient(), woher sie die index.htm nehmen soll? Ich kann weder erkennen, dass serverRouting noch handleFileRead aufgerufen wird?

Vielen Dank im Voraus für Eure Hilfe!
Werner

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>
 
const char* ssid = "<your-ssid>";
const char* password = "<your-passwd>";
 
ESP8266WebServer httpServer(80);
 
 
bool loadFromSPIFFS(String path, String dataType) {
  Serial.print("Requested page -> ");
  Serial.println(path);
  if (SPIFFS.exists(path)){
      File dataFile = SPIFFS.open(path, "r");
      if (!dataFile) {
          handleNotFound();
          return false;
      }
 
      if (httpServer.streamFile(dataFile, dataType) != dataFile.size()) {
        Serial.println("Sent less data than expected!");
      }else{
          Serial.println("Page served!");
      }
 
      dataFile.close();
  }else{
      handleNotFound();
      return false;
  }
  return true;
}
 
void serverRouting();
 
void setup(void) {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
 
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
 
  Serial.print(F("Inizializing FS..."));
  if (SPIFFS.begin()){
    Serial.println(F("done."));
  }else{
    Serial.println(F("fail."));
  }
 
  Serial.println("Set routing for http server!");
  serverRouting();
  httpServer.begin();
  Serial.println("HTTP server started");
}
 
void loop(void) {
    httpServer.handleClient();
}
 
String getContentType(String filename){
  if(filename.endsWith(F(".htm")))          return F("text/html");
  else if(filename.endsWith(F(".html")))    return F("text/html");
  else if(filename.endsWith(F(".css")))     return F("text/css");
  else if(filename.endsWith(F(".js")))      return F("application/javascript");
  else if(filename.endsWith(F(".json")))    return F("application/json");
  else if(filename.endsWith(F(".png")))     return F("image/png");
  else if(filename.endsWith(F(".gif")))     return F("image/gif");
  else if(filename.endsWith(F(".jpg")))     return F("image/jpeg");
  else if(filename.endsWith(F(".jpeg")))    return F("image/jpeg");
  else if(filename.endsWith(F(".ico")))     return F("image/x-icon");
  else if(filename.endsWith(F(".xml")))     return F("text/xml");
  else if(filename.endsWith(F(".pdf")))     return F("application/x-pdf");
  else if(filename.endsWith(F(".zip")))     return F("application/x-zip");
  else if(filename.endsWith(F(".gz")))      return F("application/x-gzip");
  return F("text/plain");
}
 
bool handleFileRead(String path){
  Serial.print(F("handleFileRead: "));
  Serial.println(path);
 
  if(path.endsWith("/")) path += F("index.html");           // If a folder is requested, send the index file
  String contentType = getContentType(path);                // Get the MIME type
  String pathWithGz = path + F(".gz");
  if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){     // If the file exists, either as a compressed archive, or normal
    if(SPIFFS.exists(pathWithGz))                           // If there's a compressed version available
      path += F(".gz");                                     // Use the compressed version
    fs::File file = SPIFFS.open(path, "r");                 // Open the file
    size_t sent = httpServer.streamFile(file, contentType); // Send it to the client
    file.close();                                           // Close the file again
    Serial.println(String(F("\tSent file: ")) + path + String(F(" of size ")) + sent);
    return true;
  }
  Serial.println(String(F("\tFile Not Found: ")) + path);
  return false;                                             // If the file doesn't exist, return false
}
 
void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += httpServer.uri();
  message += "\nMethod: ";
  message += (httpServer.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += httpServer.args();
  message += "\n";
 
  for (uint8_t i = 0; i < httpServer.args(); i++) {
    message += " " + httpServer.argName(i) + ": " + httpServer.arg(i) + "\n";
  }
 
  httpServer.send(404, "text/plain", message);
}
 
void serverRouting() {
      httpServer.onNotFound([]() {                              // If the client requests any URI
          Serial.println(F("On not found"));
        if (!handleFileRead(httpServer.uri())){                  // send it if it exists
            handleNotFound(); // otherwise, respond with a 404 (Not Found) error
        }
      });
 
      Serial.println(F("Set cache!"));
      // Serve a file with no cache so every tile It's downloaded
      httpServer.serveStatic("/configuration.json", SPIFFS, "/configuration.json","no-cache, no-store, must-revalidate");
      // Server all other page with long cache so browser chaching they
      httpServer.serveStatic("/", SPIFFS, "/","max-age=31536000");
 
 
}

ohne .on landet ein request im onNotFound und darin wird zunächst mittels handleFileRead im FS nachgesehen und wenn es die Seite nicht gibt, erst die 404/handleNotFound() aufgerufen.

Daher braucht es keine explizite .on

@noiasca, vielen Dank für deine schnelle Antwort! Trotzdem noch eine Frage:
Woher weiß denn dann onNotFound, dass es (im Beispielsketch) SPIFFS gibt (in der hier nicht gezeigten von mir geänderten Version SD Karte) und dass da im root index.htm liegt?

Bei dem von Dir veröffentlichten Code überhaupt nicht, da da keine SD drin ist.

Gruß Tommy

@Tommy56, ja, hatte ich oben beschrieben, dann eben SPIFFS. Ich ändere es, damit es keine zusätzliche Verwirrung gibt.

@noiasca. Ich glaube, ich habe es jetzt verstanden. In serverRouting() ist onNotFound() enthalten und was da hinter onNotFound in den geschweiften Klammern steht, das wird auch dann ausgeführt, wenn serverRouting() nicht aufgerufen wird. Ich schätze, das war mein Verständnisproblem.

schau dir den returncode von handleFileRead an.
die Funktion gibt true oder false retour.
Und du findest in der handleFileRead auch genau die Abfrage auf index.html für den Fall dass du das nicht angeben würdest.

@noiasca, den Teil verstehe ich, danke.
Was für mich tatsächlich neu ist, dass onNotFound() ausgeführt wird, obwohl es in einer Funktion serverRouting() steht, die nicht ausgeführt wird. Einfach weil handleClient() in loop() ausgeführt wird und es kein on() gibt.
Das ist für dich vermutlich das Normalste der Welt aber solche Fälle hatte ich in meiner bescheidenen Programmiererfahrung tatsächlich noch nicht.

die onNotFound ist ja nur dazu dazu dem Objekt übergeben zu können was im Falle von not Found ausgeführt werden soll.
Und das übergibst du dem Objekt eben einmal - im Setup.

Verstehe. Vielen Dank nochmal für deine schnelle Antwort und für deine Zeit!!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.