托管网站加载 <body> 中的错误不在代码中

Hosted Website loads with errors in <body> that are not in the code

阿霍伊,

我正在用我的“Olimex ESP-32 POE”托管一个小型 debug-website。目标是通过 JSON 发送一些内部数据,而不必使用 Arduino IDE 的 Serial-Output(背后的原因无关紧要)。

#include "Arduino.h"
#include <WiFiClient.h>
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <string>
const char* ssid = "SSID";
const char* password = "password";
int looper = 0;
int looperSpeed = 0;
int looperMode = 0;
int looperDestination = 0;
int looperETC = 0;
WebServer webserver(80);
void initWebServer();
void getSettings() {
String response = "{";
   response+= "\"speed\": \""+(String) looperSpeed+"\"";
   response+= ",\"mode\": \""+(String) looperMode+"\"";
   response+= ",\"dest\": \""+(String) looperDestination+"\"";
   response+= ",\"etc\": \""+(String) looperETC+"\"";
   if (webserver.arg("signalStrength")== "true"){
       response+= ",\"signalStrengh\": \""+String(WiFi.RSSI())+"\"";
   }
   response+="}";
   webserver.send(200, "text/json", response);
}
void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += webserver.uri();
  message += "\nMethod: ";
  message += (webserver.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += webserver.args();
  message += "\n";
  for (uint8_t i = 0; i < webserver.args(); i++) {
    message += " " + webserver.argName(i) + ": " + webserver.arg(i) + "\n";
  }
  webserver.send(404, "text/plain", message);
}
void setup() {
  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(".");
  }
  initWebServer();
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  // Set not found response
  webserver.onNotFound(handleNotFound);
  // Start server
  webserver.begin();
  Serial.println("HTTP server started");
}
void loop() {
  webserver.handleClient();
  //Some test values are changed here periodically (looperXYZ)
  }
}

以及创建网站的部分:

std::string online_output = "Test";
const char* serverIndex() {
  const char* o = online_output.c_str();
  const char* r = 
  (std::string("") +
  "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>" +
  "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>" +
     "<input type='file' name='update'>" +
          "<input type='submit' value='Update'>" +
      "</form>" +
   "<div id='prg'>Progress: 0%</div>" +
   "<div id='output' style=\"font-family: monospace; border: 1px solid black; width: 350px;min-height:398px;\">" + 
   "</div>" +
   "<script>" +
    "var id = 0;" + 
    "var removeId = 0;" + 
    "setInterval(function(){" +
    /*"var xhReq = new XMLHttpRequest();" +
    "xhReq.open('GET', '/JSON', false);" +
    "xhReq.send(null);" +
    "var jsonObject = JSON.parse(xhReq.responseText);" +*/
    "var data = {};" +
    "$.ajax({" +
      "type: 'GET'," +
      "url: '/JSON'," +
      "data: data," +
      "async: true," +
      "beforeSend: function (xhr) {" +
        "if (xhr && xhr.overrideMimeType) {" +
          "xhr.overrideMimeType('application/json;charset=utf-8');" +
        "}" +
      "}," +
      "dataType: 'json'," +
      "success: function (data) {" +
        "document.getElementById('output').innerHTML = \"<p id=\" + id + \" style='margin:0;padding:0px;'>\" + \"Mode: \" + fill(data.mode,2) + \" | Speed: \" + fill(data.speed,4) + \" | Dest: \" + fill(data.dest,4) + \" | ETC: \" + fill(data.etc,5) + \"</p>\" + document.getElementById('output').innerHTML;" +
       // "if (document.getElementById('output').offsetHeight > 400) document.getElementById('output').innerHTML = \"<p style='margin:0;padding:0px;'>\" + data.name + \"</p>\";" +
       "if (document.getElementById('output').offsetHeight > 400) { document.getElementById(removeId).remove(); removeId++;}" +
        "id++;" +
        "console.log(data);" +
      "}" +
    "});" +
    "}, 50);" +
    "function fill(n,m) { " + 
    "var pre=\"\";" +
    "var dec=10;" +
    "for(var i=1;i<m;i++) { if(n<dec) { pre+=\".\"; } dec*=10; }" +
    "pre = pre + n;" + 
    "return pre; }" + 
    "$('form').submit(function(e){" +
    "e.preventDefault();" +
    "var form = $('#upload_form')[0];" +
    "var data = new FormData(form);" +
    " $.ajax({" +
    "url: '/update'," +
    "type: 'POST'," +
    "data: data," +
    "contentType: false," +
    "processData:false," +
    "xhr: function() {" +
    "var xhr = new window.XMLHttpRequest();" +
    "xhr.upload.addEventListener('progress', function(evt) {" +
    "if (evt.lengthComputable) {" +
    "var per = evt.loaded / evt.total;" +
    "$('#prg').html('progress: ' + Math.round(per*100) + '%');" +
    "}" +
    "}, false);" +
    "return xhr;" +
    "}," +
    "success:function(d, s) {" +
    "console.log('success!')" +
   "}," +
   "error: function (a, b, c) {" +
   "}" +
   "});" +
   "});" +
   "</script>").c_str();
   return r;
}
const char* host = "esp32";
 void initWebServer() {
  if (!MDNS.begin(host)) { //http://esp32.local
    Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(1000);
    }
  }
  Serial.println("mDNS responder started");
  /*return index page which is stored in serverIndex */
  webserver.on("/", HTTP_GET, []() {
    webserver.sendHeader("Connection", "close");
    webserver.send(200, "text/html", serverIndex());
  });
  webserver.on("/JSON", HTTP_GET, []() {
    getSettings();
  });
  /*handling uploading firmware file */
  webserver.on("/update", HTTP_POST, []() {
    webserver.sendHeader("Connection", "close");
    webserver.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
    ESP.restart();
  }, []() {
    HTTPUpload& upload = webserver.upload();
    if (upload.status == UPLOAD_FILE_START) {
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      /* flashing firmware to ESP*/
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
      } else {
        Update.printError(Serial);
      }
    }
  });
  webserver.begin();
}

这是测试代码,因此之前的测试可能有 left-overs - 仅供参考。

加载网站时(目前我正在使用 Chrome)有时它可以工作,有时什么也没有加载(这个网站不工作,然后是一个空页面)有时我得到如下结果:xV­ºùÿý?øÿý?;"> 这是屏幕上唯一的输出。

详细显示如下:

<html><head></head><body>xV&shy;ºùÿý?øÿý?;"&gt;<script>var id = 0; [...the rest of the <script> part is loaded properly...]

我刚刚注意到在这些奇怪的字符前面有 364 个中心点 (·) 但我实际上无法复制它们,除了 Chrome->Inspect->"Sources"- 没有编辑器显示它们选项卡

所以基本上body坏了,出现了这些字符,目前也没有改变。

有人可以指出我自己解决这个问题的方向,以便网站始终正确加载或知道错误是什么吗?

错误似乎在以下行:

const char* r = (std::string("") + “...”).c_str();

您正在创建 std::string,它在堆上分配原始 C 字符串。然后,您将获得带有 .c_str() 的字符串的 c 表示形式。问题是字符串被释放,因为您没有将字符串分配给 std::string 类型的变量。结果,您正在访问不属于您的内存。当它的内存还没有被重用时,它工作了,但是想要被另一个程序重用,它失败了,因为你从内存中得到了基本上随机的字节。

您可以通过添加以下内容来解决问题:

auto my_str = std::string("") + “...”;

因为你不需要原始指针。

对于服务器索引函数,它应该是这样的: std::string serverIndex() {

对于您的初始化网络服务器功能:

webserver.on("/", HTTP_GET, []() {
    webserver.sendHeader("Connection", "close");
    auto r = serverIndex();
    webserver.send(200, "text/html", r.c_str());
});

免责声明:此代码未经过测试,因为它仅在应用程序中编写。