Search code examples
jsonarduinoxmlhttprequestwebserveresp32

Why is the JSON content missing from my XMLHttpRequest sent to the ESP32 Arduino webserver?


I have an ESP32 running a webserver with a UI that needs to send an object array to the ESP in a JSON. The JSON string on the user end is correct, the request is received by the esp but does not contain any JSON content. This is on the user end:

var dataWhite = [{hour: 10, minute:5, light:2000},{hour: 11, minute:10, light:2050}];
function uploadWhiteData() {
            var xhr = new XMLHttpRequest();
            xhr.open("POST", "/uploadWhiteData", true);
            xhr.setRequestHeader('Content-Type', 'application/json');
            xhr.onreadystatechange = function() {
              if (xhr.readyState == 4 && xhr.status == 200) {
                console.log(xhr.responseText);
              }
            };
            xhr.send(JSON.stringify(dataWhite));
          }

On the server end I tried getting to the JSON like this:

server.on("/uploadWhiteData", HTTP_POST, [](AsyncWebServerRequest *request) {
    if (request->hasParam("plain", true)) {
      String data = request->getParam("plain", true)->value();
      Serial.print("Incoming JSON: ");
      Serial.println(data);
    }
    request->send(200);
  });

(in this case the hasParam() always returns as false) and like this:

server.on("/uploadWhiteData", HTTP_POST, [](AsyncWebServerRequest *request) {
  request->onRequestBody([](const String& body, size_t length) {
    if (request->header("Content-Type") == "application/json") {
      String data = body;
      Serial.print("Incoming JSON: ");
      Serial.println(data);

    }
  });

  request->send(200);
});

(with this it prints "incoming json" and then nothing) I also tried with a get request, that resulted in the same outcome.


Solution

  • See setting up the server on how to create a HTTP_POST route. You can also take a look at my example here (scroll down to the section "ESP32 Web Sever Back-end Codes" where the main.cpp is shown). Basically the api should be:

    server.on("/your_route", HTTP_POST, onRequest, onFileUpload, onBody);
    

    With the async nature of the web server, when the request is received the message body is not available yet, the API handling this with 3 callback functions, each is called at the different stages of the request, the way you are doing, you are expecting the request body to be available when handling the onRequest callback, it actually only available when the onBody callback is triggered.

    void onRequest(AsyncWebServerRequest *request) {
        // dummy callback function for handling params, etc.
    }
    
    void onFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
        // dummy callback function signature, not in used in this example
    }
    
    void onBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
      
        Serial.printf("body=%s\n", (char*) data);
        // deserialise the json data, do what you want with the data
        request->send(200, "text/plain", "Ok");
    }
    
    void setup() {
      // your setup code here 
    
      server.on("/uploadWhiteData", HTTP_POST, onRequest, onFileUpload, onBody);
    
      server.begin();
    }
    
    void loop() {
    
    }