HTTP-запрос ESP32 обрабатывает ответ веб-сервера Go, но не работает на JS-серверах node.

Я настроил плату esp32 (wemos d1 r32) для отправки запроса на сервер, размещенный в моей локальной сети. Если я настрою очень простой сервер go, плата отлично обработает ответ (200 с соответствующим содержимым тела). Когда я переключаюсь на сервер Node JS, он отвечает только кодом состояния 400. Самое странное, что ни один из вызовов console.log в обработчиках маршрутов не вызывается, что указывает на то, что он завершает работу еще до того, как доберется до экспресс-сервера. Я проверил, что сервер возвращает тело & код состояния Я хочу использовать почтальона, все выглядит хорошо.

Я пробовал разные фреймворки (Koa), чтобы понять, связано ли это с Express, но и с ним это не работает. Я предполагаю, что это как-то связано с NodeJS?

Экспресс-сервер

import express from "express";

const app = express();
app.use(express.json());
app.post("/", (req, res) => {
  console.log(req.body);
  req.statusCode = 200;
  res.send("200");
});

app.get("/hello", (req, res) => {
  res.setTimeout(50000);
  console.log(req.body);
  res.setHeader("Location", "123");
  res.setHeader("Server", "123");
  req.statusCode = 200;
  res.send("200");
});

app.listen(8090, () => {
  console.log(`Example app listening on port ${8090}`);
});

Сервер Go

package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {
    for name, headers := range w.Header() {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
    fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {

    for name, headers := range req.Header {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
}

func main() {
    fmt.Println("?")

    http.HandleFunc("/hello", hello)
    http.HandleFunc("/headers", headers)

    http.ListenAndServe(":8090", nil)
}

Скрипт Arduino

#include <HTTPClient.h>
#include <WiFi.h>

// #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"

const char* ssid = "xxx";
const char* password = "xxx";

String webserver = "http://192.168.0.19:8090/hello";

String qrCode;

void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info){
  Serial.println("Connected to AP successfully!");
}

void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info){
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info){
  
  WiFi.disconnect(true);
  delay(1000);
  WiFi.mode(WIFI_STA);
  delay(1000);
  Serial.println("Disconnected from WiFi access point");
  Serial.print("WiFi lost connection. Reason: ");
  Serial.println(info.disconnected.reason);
  Serial.println("Trying to Reconnect");
  WiFi.begin(ssid, password);
  delay(5000);
}

void setup() {
  esp_log_level_set("*", ESP_LOG_VERBOSE);
  Serial.begin(9600);
  Serial2.begin(9600);
  Serial.println("Hello, ESP32!");
  Serial.println(WiFi.getMode());
  Serial.println(WiFi.macAddress());

  WiFi.onEvent(WiFiStationConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
  WiFi.onEvent(WiFiGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
  WiFi.onEvent(WiFiStationDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);

  WiFi.begin(ssid, password);
}

int cnt = 0;

void loop() {
  while (Serial2.available()) {
    int  ch = Serial2.read();
    char character = ch;
    Serial.print(ch);

    // end char
    if(character == 13){
      Serial.print("\n");
      Serial.println(qrCode);
      Serial.println(cnt);
      qrCode = "";
      cnt = 0;
      send(qrCode);
    } else {
      cnt = ch + cnt;
      qrCode = qrCode + character;
    }
  }
}

void tryWifi() {
  WiFi.begin(ssid, password);
  int status = WiFi.status();
  while (status == 6 || status == 4 || status == 5 || status == 1) {
    Serial.print("Failed to connect:"); 
    Serial.print(status);
    Serial.print("\n");
    delay(1500);
    WiFi.begin(ssid, password);
  }
}

void send(String json) {
  HTTPClient http; 

  if(http.begin(webserver)){    

      int httpCode = http.GET();
      http.addHeader("Content-Type", "application/json");
      http.addHeader("Host", "something");
      // client.print("Content-Length: ");
      delay(1500);  
      
      Serial.println(httpCode);
      Serial.println(http.getString());
      if (httpCode > 0) {
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
        if(httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = http.getString();                 
          if (payload != "false"){
            Serial.println(payload); 
            delay(1500);  
          } else {
          Serial.println("Bad Playload");
          }
        } else {
          
        }
      } else {
      Serial.println("Error sending to HTTP");
      }
      http.end(); 
    } else {
      Serial.println("?");
    } 
}

, 👍1


2 ответа


3

Ваш код Arduino выглядит следующим образом:

  int httpCode = http.GET();
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Host", "something");
  // client.print("Content-Length: ");
  delay(1500);  
  
  Serial.println(httpCode);
  Serial.println(http.getString());
  if (httpCode > 0) {

Вызов http.GET() фактически выполняет HTTP-запрос. Вы добавляете заголовки после выполнения запроса. Их нужно добавить перед ним. Таким образом, ваш код должен выглядеть так:

  // http.addHeader("Content-Type", "application/json");
  int httpCode = http.GET();

  // delay(1500); -- здесь не нужно вызывать delay()
  
  Serial.println(httpCode);
  Serial.println(http.getString());
  if (httpCode > 0) {

Код состояния HTTP 400 означает "неверный запрос". Некоторые веб-серверы возвращают этот код, если заголовки, которые они ожидают увидеть, отсутствуют или имеют бессмысленные значения. Веб-серверы Go и Javascript имеют разные реализации и могут по-разному вести себя с некорректно сформированными запросами.

Я удалил "Хост" заголовок, поскольку HTTPClient делает это за вас . И нет необходимости в вызове delay() после вызова http.GET().

Обновлено: я также закомментировал "Content-Type" заголовок, так как это не имеет смысла в запросе GET, спасибо @EdgarBonet за упоминание об этом. Я оставляю его в качестве комментария, чтобы было ясно, что любые заголовки должны быть добавлены до выполнения запроса.

,

Также обратите внимание, что "Content-Type" не имеет смысла в HTTP-запросе. OP, вероятно, означает «Принять»., @Edgar Bonet

@EdgarBonet Спасибо, хорошая мысль. На самом деле это имеет смысл для запросов PUT или POST, у которых есть тело, но не для GET, у которых никогда не должно быть тела. Я отредактировал ответ. Я подозреваю, что ОП просто добавлял случайные строки кода, чтобы посмотреть, исправили ли они свою проблему., @romkey

Спасибо за комментарии. У меня был заголовок Content-Type, так как я намеревался использовать запрос POST с телом json. Я переместил вызов, чтобы установить заголовок до того, как запрос будет запущен, хороший улов!, @Batzz


1

По прошествии неоправданного времени я понял, что запрос завершается с ошибкой 400, поскольку в конце URL-адреса нет косой черты (http://192.168.1.208:8090 -> http://192.168.1.208:8090/). Это должно быть что-то конкретное для NodeJS, так как это нормально работает на сервере go.

,