Server REST con esp8266 e esp32: richieste CORS, OPTION e GET – Parte 4

Spread the love

Ora, abbiamo fatto chiamate delle chiamate dal programma Postman, ma fare lo stesso da un browser web non è la stessa cosa.

REST server on esp8266 and esp32 CORS request OPTION and GET
REST server on esp8266 and esp32 CORS request OPTION and GET

Ci sono alcune politiche di sicurezza che non possiamo ignorare. La più importante/fastidiosa è che se si desidera effettuare una chiamata REST a un server da un client, con dominio diverso (o origine se si preferisce) si entra nel tunnel dei CORS.

Cross-Origin Resource Sharing (CORS) è un meccanismo che utilizza intestazioni HTTP aggiuntive per indicare ai browser di fornire a un’applicazione Web in esecuzione su un’origine, l’accesso a risorse selezionate da un’origine diversa. Un’applicazione Web esegue una  richiesta HTTP tra origini  quando richiede una risorsa che ha un’origine diversa (dominio, protocollo o porta) dalla propria. (Cit.)

MA che significa? 😛

Se prendi questo codice html/js

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Do get request</title>
</head>
<body>
    <div>Normal response</div>
    <div id="content"></div>
    <div>Error response</div>
    <div id="errorContent"></div>
</body>
<script>
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            // Typical action to be performed when the document is ready:
            document.getElementById("content").innerHTML = xhttp.responseText;
        }
    };
    xhttp.onerror = function () {
        document.getElementById("errorContent").innerHTML = "Status code is " + this.status + " click F12 and check what is the problem on console";
    };

    xhttp.open("GET", "http://esp8266/settings", true);
    xhttp.send();

</script>
</html>

e provi ad eseguire questa semplice GET

    xhttp.open("GET", "http://esp8266/settings", true);

Il tuo browser notifica sulla console (F12 per vederla) che tu hai un

Access to XMLHttpRequest at 'http://esp8266/settings' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

errore CORS, questo perché stai tentando di fare una chiamata ad un end-point REST da un’origine differente.

Gestire una chiamata CORS in GET

Server REST con esp8266 ed esp32 il tunnel dei CORS
Server REST con esp8266 ed esp32 il tunnel dei CORS

Il tuo primo tentativo è quello di aggiungere l’intestazione per specificare che il server deve accettare tutte le origini e i verbi, quindi è possibile aggiungere dopo l’open e prima della send

    xhttp.setRequestHeader('Access-Control-Allow-Headers', '*');
    xhttp.setRequestHeader('Access-Control-Allow-Origin', '*');

Ma ora abbiamo un nuovo errore

OPTIONS http://esp8266/settings 404 (Not Found)

Access to XMLHttpRequest at 'http://esp8266/settings' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Qui puoi verificare che il client non tenti di eseguire la richiesta GET ma un OPTION e di conseguenza non trova quell’end point.

E l’errore specifica che “Response to preflight request doesn't pass access control check“, e questo è correlato al precedente 404 su OPTION.

Nelle chiamate CORS, con il verbo OPTIONS viene inviata una richiesta di verifica preliminare, in modo che il server possa rispondere se è accettabile inviare la richiesta con questi parametri.

E ora aggiungeremo l’endpoint OPTION al server REST.

E lo aggiungeremo allo sketch esp8266

void sendCrossOriginHeader(){
	server.send(204);
}

// Define routing
void restServerRouting() {
    server.on("/", HTTP_GET, []() {
    	server.send(200, F("text/html"),
            F("Welcome to the REST Web Server"));
    });
    server.on(F("/helloWorld"), HTTP_GET, getHelloWord);
    server.on(F("/settings"), HTTP_OPTIONS, sendCrossOriginHeader);
    server.on(F("/settings"), HTTP_GET, getSettings);
}

Ma otteniamo ancora un errore

Access to XMLHttpRequest at 'http://esp8266/settings' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Il problema è che l’OPTION, anche se, ha una risposta in 204 (tutti i 2xx sono risposte positive) non restituisce l’intestazione corretta, quindi aggiungeremo l’intestazione alla risposta del GET.

	httpRestServer.sendHeader(F("Access-Control-Allow-Origin"), F("*"));
	httpRestServer.sendHeader(F("Access-Control-Max-Age"), F("600"));
	httpRestServer.sendHeader(F("Access-Control-Allow-Methods"), F("PUT,POST,GET,OPTIONS"));
	httpRestServer.sendHeader(F("Access-Control-Allow-Headers"), F("*"));

E ora finalmente

Normal response
{"ip":"192.168.1.129","gw":"192.168.1.1","nm":"255.255.255.0"}
Error response

Ok, abbiamo finalmente una chiamata con verbo GET in CORS.

Per esp32 devi solo cambiare questi include

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

in

#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>

e le informazioni sul chip così

      doc["chipRevision"] = ESP.getChipRevision();
      doc["flashChipMode"] = ESP.getFlashChipMode();
      doc["flashChipSize"] = ESP.getFlashChipSize();
      doc["flashChipSpeed"] = ESP.getFlashChipSpeed();

Ecco lo sketch completo

/*
 *  Json parametric GET REST response with ArduinoJSON library
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/
 *
 */

#include "Arduino.h"
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ArduinoJson.h>

const char* ssid = "<YOUR-SSID>";
const char* password = "<YOUR-PASSWD>";

ESP8266WebServer server(80);

void setCrossOrigin(){
	server.sendHeader(F("Access-Control-Allow-Origin"), F("*"));
	server.sendHeader(F("Access-Control-Max-Age"), F("600"));
	server.sendHeader(F("Access-Control-Allow-Methods"), F("PUT,POST,GET,OPTIONS"));
	server.sendHeader(F("Access-Control-Allow-Headers"), F("*"));
};


// Serving Hello world
void getHelloWord() {
	  DynamicJsonDocument doc(512);
	  doc["name"] = "Hello world";

	  Serial.print(F("Stream..."));
	  String buf;
	  serializeJson(doc, buf);
	  server.send(200, "application/json", buf);
	  Serial.print(F("done."));
}

// Serving Hello world
void getSettings() {
	setCrossOrigin();
//
	  // Allocate a temporary JsonDocument
	  // Don't forget to change the capacity to match your requirements.
	  // Use arduinojson.org/v6/assistant to compute the capacity.
	//  StaticJsonDocument<512> doc;
	  // You can use DynamicJsonDocument as well
	  DynamicJsonDocument doc(512);

	  doc["ip"] = WiFi.localIP().toString();
	  doc["gw"] = WiFi.gatewayIP().toString();
	  doc["nm"] = WiFi.subnetMask().toString();

	  if (server.arg("signalStrength")== "true"){
		  doc["signalStrengh"] = WiFi.RSSI();
	  }

	  if (server.arg("chipInfo")== "true"){
		  doc["chipId"] = ESP.getChipId();
		  doc["flashChipId"] = ESP.getFlashChipId();
		  doc["flashChipSize"] = ESP.getFlashChipSize();
		  doc["flashChipRealSize"] = ESP.getFlashChipRealSize();
	  }
	  if (server.arg("freeHeap")== "true"){
		  doc["freeHeap"] = ESP.getFreeHeap();
	  }

	  Serial.print(F("Stream..."));
	  String buf;
	  serializeJson(doc, buf);
	  server.send(200, F("application/json"), buf);
	  Serial.print(F("done."));
}

void sendCrossOriginHeader(){
	Serial.println(F("sendCORSHeader"));
    setCrossOrigin();
	server.send(204);
}

// Define routing
void restServerRouting() {
    server.on("/", HTTP_GET, []() {
    	server.send(200, F("text/html"),
            F("Welcome to the REST Web Server"));
    });
    server.on(F("/helloWorld"), HTTP_GET, getHelloWord);
    server.on(F("/settings"), HTTP_OPTIONS, sendCrossOriginHeader);
    server.on(F("/settings"), HTTP_GET, getSettings);

}

// Manage not found URL
void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

void setup(void) {
  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(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // Activate mDNS this is used to be able to connect to the server
  // with local DNS hostmane esp8266.local
  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
  }

  // Set server routing
  restServerRouting();
  // Set not found response
  server.onNotFound(handleNotFound);
  // Start server
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();
}

Grazie

  1. Server REST su esp8266 o esp32: introduzione
  2. Server REST su esp8266 o esp32: GET e formattazione JSON
  3. Server REST su esp8266 o esp32: POST, PUT, PATCH, DELETE
  4. Server REST su esp8266 o esp32: richieste CORS, OPTION e GET
  5. Server REST su esp8266 o esp32: richieste CORS, OPTION e POST

Spread the love

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *