Как правильно объявить случайный URL-адрес в ESPAsyncWebServer?

Я пытаюсь создать одноразовый URL-адрес со случайной строкой, например, чтобы предоставить пользователю одноразовый доступ к дому с интеллектуальным замком. У меня есть функция для создания фиксированной строки из 8 символов и следующего кода со случайным адресом.

  controlserver.on(userurl, HTTP_GET, [](AsyncWebServerRequest *request){
   if(urlactive==true){
   if(request->authenticate("admin","admin")){  
      running=true; 
      request->send(200); 
      
      Serial.println("entered"); 
      urlactive=false;
      }else {
       return request->requestAuthentication();
      }
   }else{
       generateRandomString();
       request->send(200, "text/html", HTML_CSS_STYLING+ "<body>"
    "<br><h1 style=\"color:white; direction: rtl;\">LInk is not valid!</h1></body>" );
       
   }
  });

Следующая функция генерирует userurl, который представляет собой const char*, поскольку это требование библиотеки ESPAsyncWebServer.

void generateRandomString() {
  const char specialChars[] = "0123456789";
  const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  String randomStringurl = "";
  String keppmyuserpass = "";
  randomStringurl += letters[random(26, 51)];
  for (int i = 0; i < 6; i++) {
    randomStringurl += letters[random(0, 51)];
  }
  randomStringurl += specialChars[random(0, 9)];
  Serial.println(randomStringurl);
  userurl = randomStringurl.c_str();

}

все работает нормально, когда я ввожу сгенерированную строку, которую конвертирую в const char* с помощью c_str(), но я заметил ошибку, когда частично случайно скопировал строку и к моему большому удивлению, я увидел, что появилось всплывающее окно для имени пользователя и пароля, но я этого не ожидал, потому что у меня есть функция notFound, которая будет показывать ошибку, когда введенный URL-адрес не отображается. не существует. ввел разные строки, но проблема не устранена. Я думаю, что возникла проблема с преобразованием строки в const char* с использованием c_str(). но так ли это?

Вот совместимый код, режим Wi-Fi — STA WiFi.mode(WIFI_STA); :

#include <Arduino.h>
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>

const char* userurl;
String  randomStringurl = "";
String  keppmyuserpass = "";
  
AsyncWebServer server(80);

const char* ssid = "your_ssid";
const char* password = "your_password";

const char* PARAM_MESSAGE = "message";

void notFound(AsyncWebServerRequest *request) {
    request->send(404, "text/plain", "Not found");
}
void generateRandomString() {
  const char specialChars[] = "0123456789";
  const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  randomStringurl = "";
  keppmyuserpass = "";
  randomStringurl += letters[random(26, 51)];
  for (int i = 0; i < 6; i++) {
    randomStringurl += letters[random(0, 51)];
  }
  randomStringurl += specialChars[random(0, 9)];
  Serial.println(randomStringurl);
  userurl = randomStringurl.c_str();
  keppmyuserpass = randomStringurl;
  keppmyuserpass += letters[random(26, 51)];
  keppmyuserpass += specialChars[random(0, 9)];
  Serial.println(keppmyuserpass);

}
void setup() {

    Serial.begin(115200);
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    if (WiFi.waitForConnectResult() != WL_CONNECTED) {
        Serial.printf("WiFi Failed!\n");
        return;
    }

    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());

    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
        request->send(200, "text/plain", "Hello, world");
    });

    // Отправляем запрос GET на адрес <IP>/get?message=<message>
    server.on(userurl, HTTP_GET, [] (AsyncWebServerRequest *request) {
        String message;
        if (request->hasParam(PARAM_MESSAGE)) {
            message = request->getParam(PARAM_MESSAGE)->value();
        } else {
            message = "No message sent";
        }
        request->send(200, "text/plain", "Hello, GET: " + message);
    });

    // Отправляем POST-запрос на адрес <IP>/post с сообщением в поле формы, установленным в <message>
    server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
        String message;
        if (request->hasParam(PARAM_MESSAGE, true)) {
            message = request->getParam(PARAM_MESSAGE, true)->value();
        } else {
            message = "No message sent";
        }
        request->send(200, "text/plain", "Hello, POST: " + message);
    });

    server.onNotFound(notFound);

    server.begin();
    generateRandomString();
}

void loop() {
}

А вот код из библиотеки ESPAsyncWebServer.h, который требует, чтобы url был const char* uri, что я делаю не так?

AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);

Новое обновление: Итак, я забыл добавить "/" перед URL-адресом, это решило проблему, и вот новая функция:

void generateRandomString() {
  const char specialChars[] = "0123456789";
  const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  randomStringurl = "";
  keppmyuserpass = "";
  randomStringurl +="/";
  randomStringurl += letters[random(26, 51)];
  for (int i = 0; i < 6; i++) {
    randomStringurl += letters[random(0, 51)];
  }
  randomStringurl += specialChars[random(0, 9)];
  Serial.println(randomStringurl);
  userurl = randomStringurl.c_str();
  keppmyuserpass = randomStringurl;
  keppmyuserpass += letters[random(26, 51)];
  keppmyuserpass += specialChars[random(0, 9)];
  OTUPC = keppmyuserpass.c_str();
  Serial.println(keppmyuserpass);

}

Но когда я обновляю ссылку, новая ссылка не распознается, а первая сгенерированная ссылка работает. Я думаю, что @6v6gt может быть прав в этом вопросе: «Может ли быть так, что значение userurl должно быть известно, когда setup() запускается и обратный вызов регистрируется? "

, 👍1

Обсуждение

useurl будет указывать на массив символов, которого больше нет, поскольку локальный объект String randomStringurl создается в стеке и уничтожается при выходе из функции. Динамически созданный внутри него массив также будет удален. Таким образом, это будет работать только до тех пор, пока эта часть памяти не будет перезаписана (чего вы не можете знать)., @chrisl

Хотя ваша проблема к этому не совсем подходит. Пожалуйста, покажите нам минимальный компилируемый пример кода, показывающий проблему, а не просто несколько фрагментов., @chrisl

@chrisl Я создал глобальную строку `randomStringurl`, но проблема все еще сохраняется. Я добавил совместимый код. Разве символ с нулевым завершением в конце char не является источником проблемы? потому что обычно я определяю URL-адрес типа `"/someurl"`, а не просто передавая `const chr* someurl``., @M A K

const char* userurl; . . . ; userurl = randomStringurl.c_str(); Копия содержимого переменной String не сохраняется. Это просто сохранение копии указателя на область, которая может исчезнуть (как уже отмечалось). Вам нужно создать глобальный (или статический) буфер, что-то вроде char userurl[80], а затем использовать strcpy() для его заполнения. Может ли быть также так, что значение userurl должно быть известно при запуске setup() и регистрации обратного вызова?, @6v6gt

@6v6gt, как я могу узнать, что значение `userurl` должно быть известно?, @M A K

полезен ли здесь random()? вам следует использовать CSPRNG ESP, если не для генерации, то хотя бы для randomSeed()..., @dandavis


1 ответ


Лучший ответ:

1

6v6gt прав: URI для обработчика должен быть известен, когда вы используете функцию on() для установки обработчика. Внутри библиотеки функция on() вызывает setUri(const String& uri), который сохраняет uri во внутренних классах >Строковая переменная-член. Таким образом, дескриптор не заметит, если первоначально использованная переменная изменится.

Я думаю, что наиболее элегантным решением здесь было бы использование регулярного выражения для URI вашего обработчика (регулярное выражение — это строковое выражение, описывающее шаблон, который может соответствовать другим строкам). Это позволит обработчику сопоставить любой URI, соответствующий этому регулярному выражению. Внутри обработчика вы можете получить соответствующую часть URI и проверить, действителен ли он в данный момент. Если да, вы показываете свой контент, в противном случае вы выдаете ошибку (например, 404 Not Found).

Третий ответ на этот вопрос содержит фрагмент кода:

#include <uri/UriRegex.h>

web_server.on(UriRegex("/home/([0-9]+)/first"), HTTP_GET, [&]() {
    web_server.send(200, "text/plain", "Hello from first! URL arg: " + web_server.pathArg(0));
});

Часть URI, заключенная в скобки, представляет собой группу регулярных выражений, которая затем становится доступной через web_server.pathArg(0). Ваш текущий код создает строку с одной заглавной буквой, 6 строчными буквами и одной цифрой, то есть группу регулярных выражений ([AZ][az]{6}[0-9]). Вы можете прочитать о том, как работают регулярные выражения, в Интернете.

Когда вы просматриваете этот репозиторий ESPAsyncWebServer (я не конечно, какая у вас версия), в файле Readme в самом конце в разделе "переменная пути" вы найдете этот фрагмент:

server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
  String sensorId = request->pathArg(0);
});

И обратите внимание: поддержка регулярных выражений должна быть включена.

Поскольку я это не проверял, предлагаю вам попробовать оба метода и посмотреть, какой подойдет именно вам.

,

спасибо, это полезно., @M A K