Обработка POST запроса от клиента

Привет! Всю голову сломал, нужна помощь.
с клиента приходит такой POST-запрос:

POST /save HTTP/1.1
Host: 192.168.0.17
Connection: keep-alive
Content-Length: 47
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.0.17
Referer: http://192.168.0.17/secure.htm
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7

field1=TTK-191&field2=ghbdtnbr&newField=newValue

имеется библиотека:


#pragma once
#include <Arduino.h>
#include <Ethernet.h>
#include <SD.h>
#define MAX_REQUEST_HANDLERS 10

class ArdClient {
  private:
    EthernetServer server;
    EthernetClient client;
    byte _MACAddress[6];
    byte _IPAddress[4];
    int _port;
    typedef void (*RequestHandler)(EthernetClient client);
    struct RequestHandlerEntry {
      const char* path;
      RequestHandler handler;
    };
    typedef void (*PostHandler)(EthernetClient client, String body);
    struct PostHandlerEntry {
      const char* path;
      PostHandler handler;
    };
    RequestHandlerEntry requestHandlers[MAX_REQUEST_HANDLERS];
    PostHandlerEntry postHandlers[MAX_REQUEST_HANDLERS];
    int numRequestHandlers = 0;
    int numPostHandlers = 0;
  public:
    ArdClient(int port = 80);
    void begin(byte MACAddress[], byte IPAddress[], byte subnetMask[]) {
      Ethernet.begin(MACAddress, IPAddress, subnetMask);
      memcpy(_MACAddress, MACAddress, 6);
      memcpy(_IPAddress, IPAddress, 4);
    }
    void begin(byte MACAddress[], byte IPAddress[]) {
      Ethernet.begin(MACAddress, IPAddress);
      memcpy(_MACAddress, MACAddress, 6);
      memcpy(_IPAddress, IPAddress, 4);
    }
    void sendHeader(EthernetClient client) {
      client.println("HTTP/1.1 200 OK");
      client.println("Content-Type: text/html");
      client.println();
    }
    void onRequest(const char* path, RequestHandler handler) {
      if (numRequestHandlers < MAX_REQUEST_HANDLERS) {
        requestHandlers[numRequestHandlers].path = path;
        requestHandlers[numRequestHandlers].handler = handler;
        numRequestHandlers++;
      }
    }
    void onPost(const char* path, PostHandler handler) {
      if (numPostHandlers < MAX_REQUEST_HANDLERS) {
        postHandlers[numPostHandlers].path = path;
        postHandlers[numPostHandlers].handler = handler;
        numPostHandlers++;
      }
    }
    void tick() {
      EthernetClient incomingClient = server.available();
      if (incomingClient) {
        String request = "";
        while (incomingClient.connected()) {
          if (incomingClient.available()) {
            char c = incomingClient.read();
            Serial.print(c); // выводим полный GET/POST запрос в Serial
            request += c;
            if (c == '\n') {
              break;
            }
          }
        }

        String path = request.substring(request.indexOf(' ') + 1, request.lastIndexOf(' '));
        if (request.startsWith("POST")) {
          // If it's a POST request, extract body from the request
          String body = request.substring(request.lastIndexOf("\r\n\r\n") + 4);
          handlePost(incomingClient, path.c_str(), body);
        } else if (request.startsWith("GET")) {
          handleGet(incomingClient, path.c_str());
        }
        incomingClient.stop();
      }
    }

    void handleGet(EthernetClient client, const char* path) {
      if (path[0] != '/') { // проверяем наличие символа "/" в начале строки path
        client.println("HTTP/1.1 404 Not Found");
        client.println("Content-Type: text/html");
        client.println();
        client.println("<h1>404 Not Found</h1>");
        return;
      }
      for (int i = 0; i < numRequestHandlers; i++) {
        Serial.print("requestHandlers[i].path = "); Serial.println(requestHandlers[i].path);
        Serial.print("path = "); Serial.println(path);
        if (strstr(path, requestHandlers[i].path) != NULL
            && strstr(path, requestHandlers[i].path) == path
            && strlen(path) == strlen(requestHandlers[i].path))  {
          Serial.println("OK");
          requestHandlers[i].handler(client);
          return;
        }
      }
      if (numRequestHandlers >= MAX_REQUEST_HANDLERS) {
        client.println("HTTP/1.1 500 Internal Server Error");
        client.println("Content-Type: text/html");
        client.println();
        client.println("<h1>500 Internal Server Error</h1>");
        return;
      }
      client.println("HTTP/1.1 404 Not Found");
      client.println("Content-Type: text/html");
      client.println();
      client.println("<h1>404 Not Found</h1>");
    }
    void handlePost(EthernetClient client, const char* path, String body) {
      for (int i = 0; i < numPostHandlers; i++) {
        Serial.print("postHandlers[i].path = "); Serial.println(postHandlers[i].path);
        Serial.print("path = "); Serial.println(path);
        if (strstr(path, postHandlers[i].path) != NULL
            && strstr(path, postHandlers[i].path) == path
            && strlen(path) == strlen(postHandlers[i].path)) {
          Serial.println("OK");
          postHandlers[i].handler(client, body);
          return;
        }
      }
      if (numPostHandlers >= MAX_REQUEST_HANDLERS) {
        client.println("HTTP/1.1 500 Internal Server Error");
        client.println("Content-Type: text/html");
        client.println();
        client.println("<h1>500 Internal Server Error</h1>");
        return;
      }
      client.println("HTTP/1.1 404 Not Found");
      client.println("Content-Type: text/html");
      client.println();
      client.println("<h1>404 Not Found</h1>");
    }
};

ArdClient::ArdClient(int port) : _port(port), server(port) {}

и основной код программы:

#include "ArdClient.h"
byte mac[] = {0x4C, 0xD3, 0xAF, 0x1B, 0xF8, 0x99};
byte ip[] = {192, 168, 0, 17};
byte subnet[] = {255, 255, 255, 0};
ArdClient server(80);
bool SD_OK;
void setup() {
  Serial.begin(9600);
  server.begin(mac, ip, subnet);
  if (!SD.begin(4)) return;
  Serial.println("SD OK");
  SD_OK = true;
  // добавление обработчиков GET запросов
  server.onRequest("/", handleIndex);
  server.onRequest("/index.htm", handleIndex);
  server.onRequest("/secure.htm", handleSecure);
  server.onPost("/save", handleSave);
}

void loop() {
  if (!SD_OK) return;
  server.tick();
}
void handleIndex(EthernetClient client) {
  File webFile = SD.open("htm/index.htm");
  if (webFile) {
    server.sendHeader(client);
    while (webFile.available()) {
      client.write(webFile.read());
    }
    webFile.close();
  } else {
    server.sendHeader(client);
    client.println("<h1>File not found</h1>");
  }
}

void handleSecure(EthernetClient client) {
  File webFile = SD.open("htm/secure.htm");
  if (webFile) {
    server.sendHeader(client);
    while (webFile.available()) {
      client.write(webFile.read());
    }
    webFile.close();
  } else {
    server.sendHeader(client);
    client.println("<h1>File not found</h1>");
  }
}
void handleSave(EthernetClient client, String body) {
  Serial.print("POST /save ");
  Serial.print("body = ");Serial.println(body);
  // здесь можно произвести сохранение полученных данных
  server.sendHeader(client);
  client.print("<html><body><h1>POST request saved</h1></body></html>");
}

проблема в том, что в ф-ции handleSave(EthernetClient client, String body);
body возвращает “T /save HTTP/1.1”, а мне нужно чтобы body = “field1=TTK-19&field2=ghbdtnbr&newField=newValue”. Не пойму где ошибка в библиотеке.
Люди добрые, помогите, всю голову сломал. Спасибо!

Ошибка в библиотеке в том что ты вообще полез ее писать в крайне недружелюбной для разработчика среде, не будучи достаточно опытным программистом. Возьми готовую и не усложняй себе жизнь. А если хочешь написать чисто в учебных целях, то пиши на ПК.

я примерно знаю где ошибка, она в методе tick(), а именно - метод завершается сразу после строки:
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
после нее клиент отправляет пустую строку, а уже после нее идет сама строка с данными, длиной Content-Length: 47.
и вот эту проблему я не могу никак решить.

Даже хуже. Ваш метод tick() завершается после любого перевода строки:

Поэтому от всего запроса вы и имеете только первую строку:

1 лайк
  if (request.endsWith("\r\n\r\n")) {
        break;
 }

может так?

вот так работает:


#pragma once
#include <Arduino.h>
#include <Ethernet.h>
#include <SD.h>
#define MAX_REQUEST_HANDLERS 10

class ArdClient {
  private:
    EthernetServer server;
    EthernetClient client;
    byte _MACAddress[6];
    byte _IPAddress[4];
    int _port;
    typedef void (*RequestHandler)(EthernetClient client);
    struct RequestHandlerEntry {
      const char* path;
      RequestHandler handler;
    };
    typedef void (*PostHandler)(EthernetClient client, String body);
    struct PostHandlerEntry {
      const char* path;
      PostHandler handler;
    };
    RequestHandlerEntry requestHandlers[MAX_REQUEST_HANDLERS];
    PostHandlerEntry postHandlers[MAX_REQUEST_HANDLERS];
    int numRequestHandlers = 0;
    int numPostHandlers = 0;
  public:
    ArdClient(int port = 80);
    void begin(byte MACAddress[], byte IPAddress[], byte subnetMask[]) {
      Ethernet.begin(MACAddress, IPAddress, subnetMask);
      memcpy(_MACAddress, MACAddress, 6);
      memcpy(_IPAddress, IPAddress, 4);
    }
    void begin(byte MACAddress[], byte IPAddress[]) {
      Ethernet.begin(MACAddress, IPAddress);
      memcpy(_MACAddress, MACAddress, 6);
      memcpy(_IPAddress, IPAddress, 4);
    }
    void sendHeader(EthernetClient client) {
      client.println("HTTP/1.1 200 OK");
      client.println("Content-Type: text/html");
      client.println();
    }
    void onRequest(const char* path, RequestHandler handler) {
      if (numRequestHandlers < MAX_REQUEST_HANDLERS) {
        requestHandlers[numRequestHandlers].path = path;
        requestHandlers[numRequestHandlers].handler = handler;
        numRequestHandlers++;
      }
    }
    void onPost(const char* path, PostHandler handler) {
      if (numPostHandlers < MAX_REQUEST_HANDLERS) {
        postHandlers[numPostHandlers].path = path;
        postHandlers[numPostHandlers].handler = handler;
        numPostHandlers++;
      }
    }
	int getContentLength(String request) {
      int contentLength = 0;
      int startIndex = request.indexOf("Content-Length:");
      if (startIndex != -1) {
        startIndex += 16;
        int endIndex = request.indexOf('\r', startIndex);
        if (endIndex != -1) {
          String lengthStr = request.substring(startIndex, endIndex);
          contentLength = lengthStr.toInt();
        }
      }
      return contentLength;
    }

    String getPath(String request) {
      int firstSpaceIndex = request.indexOf(' ');
      int secondSpaceIndex = request.indexOf(' ', firstSpaceIndex + 1);
      return request.substring(firstSpaceIndex + 1, secondSpaceIndex);
    }
    void tick() {
      EthernetClient incomingClient = server.available();
      if (incomingClient) {
        String request = "";
        while (incomingClient.connected() && incomingClient.available()) {
          char c = incomingClient.read();
          Serial.print(c); // выводим полный GET/POST запрос в Serial
          request += c;
          if (request.endsWith("\r\n\r\n")) {
            break;
          }
        }

        if (request.startsWith("POST")) {
          String body = "";
          while (incomingClient.connected() && incomingClient.available()) {
            char c = incomingClient.read();
            body += c;
            if (body.length() >= getContentLength(request)) { // getContentLength - функция, которая возвращает значение Content-Length из заголовков запроса.
              break;
            }
          }
          handlePost(incomingClient, getPath(request), body);
        } else if (request.startsWith("GET")) {
          handleGet(incomingClient, getPath(request).c_str());
        }
        incomingClient.stop();
      }
    }

    void handleGet(EthernetClient client, const char* path) {
      if (path[0] != '/') { // проверяем наличие символа "/" в начале строки path
        client.println("HTTP/1.1 404 Not Found");
        client.println("Content-Type: text/html");
        client.println();
        client.println("<h1>404 Not Found</h1>");
        return;
      }
      for (int i = 0; i < numRequestHandlers; i++) {
        Serial.print("requestHandlers[i].path = "); Serial.println(requestHandlers[i].path);
        Serial.print("path = "); Serial.println(path);
        if (strstr(path, requestHandlers[i].path) != NULL
            && strstr(path, requestHandlers[i].path) == path
            && strlen(path) == strlen(requestHandlers[i].path))  {
          Serial.println("OK");
          requestHandlers[i].handler(client);
          return;
        }
      }
      if (numRequestHandlers >= MAX_REQUEST_HANDLERS) {
        client.println("HTTP/1.1 500 Internal Server Error");
        client.println("Content-Type: text/html");
        client.println();
        client.println("<h1>500 Internal Server Error</h1>");
        return;
      }
      client.println("HTTP/1.1 404 Not Found");
      client.println("Content-Type: text/html");
      client.println();
      client.println("<h1>404 Not Found</h1>");
    }
    
	void handlePost(EthernetClient client, String path, String body) {
      for (int i = 0; i < numPostHandlers; i++) {
        Serial.print("postHandlers[i].path = "); Serial.println(postHandlers[i].path);
        Serial.print("path = "); Serial.println(path);
        if (strstr(path.c_str(), postHandlers[i].path) != NULL
            && strstr(path.c_str(), postHandlers[i].path) == path.c_str()
            && path.length() == strlen(postHandlers[i].path)) {
          Serial.println("OK");
          postHandlers[i].handler(client, body);
          return;
        }
      }
      if (numPostHandlers >= MAX_REQUEST_HANDLERS) {
        client.println("HTTP/1.1 500 Internal Server Error");
        client.println("Content-Type: text/html");
        client.println();
        client.println("<h1>500 Internal Server Error</h1>");
        return;
      }
      client.println("HTTP/1.1 404 Not Found");
      client.println("Content-Type: text/html");
      client.println();
      client.println("<h1>404 Not Found</h1>");
    }
};

ArdClient::ArdClient(int port) : _port(port), server(port) {}

уверены?
Я что-то сомневаюсь, глядя на код.
Но если работает - и ладно.