Arduino POST request to Laravel API

Hi couple of days I’am trying to start working simple project with Arduino Uno Wifi rev2. The scope is to read rfid data from card or chip, send rfid code to webserver and read response from it. Response will contain name of user which is assigned to sent rfid code. I’m trying to achieve this with sending POST request from Arduino to laravel API - and after that from laravel will be send response with name of the rfid user. But I didn’t even start with reading rfid data because I’m stuck just with sending POST request to webserver and to read response from server.

This is the Arduino code:

#include <WiFiNINA.h>
#include <ArduinoHttpClient.h>

char ssid[] = "SSID_SECRET";
char pass[] = "PASS_SECRET";
int port = 80;
const char serverAddress[] = "https://www.schedy.sk";  // server name

WiFiClient wifi;
HttpClient client = HttpClient(wifi, serverAddress, port);

int status = WL_IDLE_STATUS;

void setup() {
  Serial.begin(9600);
   while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass); 
    delay(5000);
  }
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  IPAddress ip = WiFi.localIP();
  IPAddress gateway = WiFi.gatewayIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
}

void loop() {
  Serial.println("making POST request");
  String postData = "rfid=abcde&test=12";
  Serial.print("Post Data Length: ");
  Serial.println(postData.length());

  client.beginRequest();
  client.post("/api/rfids");
  client.sendHeader("Content-Type", "application/x-www-form-urlencoded");
  client.sendHeader("Content-Length", postData.length());
  //client.sendHeader("X-Custom-Header", "custom-header-value");
  client.beginBody();
  client.print(postData);
  client.endRequest();

  // read the status code and body of the response
  int statusCode = client.responseStatusCode();
  String response = client.responseBody();

  Serial.print("Status code: ");
  Serial.println(statusCode);
  Serial.print("Response: ");
  Serial.println(response);

  Serial.println("Wait five seconds");
  delay(5000);
}

Laravel API. Just for testing purposes I’ve defined simple routes in /routes/api.php:

Route::middleware('auth:api')->get('/user', function (Request $request)) {
   return $request->user();
});

Route::get('/rfids', 'RfidController@index');
Route::post('/rfids', 'RfidController@store');

And in RfidController.php is:

public function index()
{
   return "get test";
}

public function store(Request $request)
{
   return response("post test")
}

I’ve tried post and get requests with https://reqbin.com/ for url: https://www.schedy.sk/api/rfids. Everything looks fine but with Arduino I’m still getting status code 400:

making POST request
Post Data Length: 17
Status code: 400
Response: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.

</p>
<hr>
<address>Apache/2.4.18 (Ubuntu) Server at kmbtwo.vps.websupport.sk Port 80</address>
</body></html>

Wait five seconds

I’m quite desperate with this problem. I tried several libraries and procedures but still getting 400 status code or -3. In server - apache2 logs I cannot get more information about the problem … just that that there is attempt to make POST request to /api/rfids but answer is 400. Dont know the reason. Can somebody help me?

  client.beginBody();
  client.print(postData);
  client.endRequest();

I'm not too familiar with HTML but shouldn't there be a "client.endBody()"?

arduino_new:
I'm not too familiar with HTML but shouldn't there be a "client.endBody()"?

I tried that but looks like endBody() doesn't exist.

  client.beginBody();
  client.print(postData);
  client.endBody();
  client.endRequest();

After this I get: error: 'class HttpClient' has no member named 'endBody'; did you mean 'beginBody'?

Have you tried using a "Get" request? The server is responding, so experiment to see what it doesn't like.

zoomkat:
Have you tried using a “Get” request? The server is responding, so experiment to see what it doesn’t like.

Hi I tried that right now but the result is the same as with POST. Still 400 status code :frowning:

#include <WiFiNINA.h>
#include <ArduinoHttpClient.h>

char ssid[] = "SSID_SECRET";
char pass[] = "PASS_SECRET";
int port = 80;
const char serverAddress[] = "https://www.schedy.sk";  // server name

WiFiClient wifi;
HttpClient client = HttpClient(wifi, serverAddress, port);

int status = WL_IDLE_STATUS;

void setup() {
  Serial.begin(9600);
   while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass); 
    delay(5000);
  }
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  IPAddress ip = WiFi.localIP();
  IPAddress gateway = WiFi.gatewayIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
}

void loop() {

  //GET Request
  Serial.println("making GET request");
  client.get("/api/rfids");

  // read the status code and body of the response
  int statusCode = client.responseStatusCode();
  String response = client.responseBody();

  Serial.print("Status code: ");
  Serial.println(statusCode);
  Serial.print("Response: ");
  Serial.println(response);
  Serial.println("Wait five seconds");
  delay(5000);
}

Result:

making GET request
Status code: 400
Response: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.

</p>
<p>Additionally, a 400 Bad Request
error was encountered while trying to use an ErrorDocument to handle the request.</p>
<hr>
<address>Apache/2.4.18 (Ubuntu) Server at kmbtwo.vps.websupport.sk Port 80</address>
</body></html>

Wait five seconds

Have you taken into account the "s" in the "https" below?

"https://www.schedy.sk";

zoomkat:
Have you taken into account the “s” in the “https” below?

"https://www.schedy.sk";

Hi I tried port 80 and 443(for https) as well. The response with port 80 is as follows and it doesnt matter if the url is http://www.schedy.sk or https://www.schedy.sk

<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.

</p>
<p>Additionally, a 400 Bad Request
error was encountered while trying to use an ErrorDocument to handle the request.</p>
<hr>
<address>Apache/2.4.18 (Ubuntu) Server at kmbtwo.vps.websupport.sk Port 80</address>
</body></html>

When using 443 port together with https://www.schedy.sk I get this result:

<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.

Reason: You're speaking plain HTTP to an SSL-enabled server port.

 Instead use the HTTPS scheme to access this URL, please.

</p>
<hr>
<address>Apache/2.4.18 (Ubuntu) Server at restauracia.autorotos.sk Port 443</address>
</body></html>

with http://www.schedy.sk I get 400 status code but without response body. These tests I’m doing with GET method which I wrote above.

It must be something really obvious which I cannot see :o. With https://reqbin.com/ or if you try simple to click on https://www.schedy.sk/api/rfids you will get normal answer from server - 200 and response “get test”.

“It must be something really obvious which I cannot see”

There is a significant difference difference between http and https. The security part adds a lot of overhead. If you have a wifi board, you can look at the examples. Below is just some wifi example code that apparently can handle https operations.

/*
    HTTP over TLS (HTTPS) example sketch

    This example demonstrates how to use
    WiFiClientSecure class to access HTTPS API.
    We fetch and display the status of
    esp8266/Arduino project continuous integration
    build.

    Limitations:
      only RSA certificates
      no support of Perfect Forward Secrecy (PFS)
      TLSv1.2 is supported since version 2.4.0-rc1

    Created by Ivan Grokhotkov, 2015.
    This example is in public domain.
*/

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK  "your-password"
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

const char* host = "api.github.com";
const int httpsPort = 443;

// Use web browser to view and copy
// SHA1 fingerprint of the certificate
const char fingerprint[] PROGMEM = "5F F1 60 31 09 04 3E F2 90 D2 B0 8A 50 38 04 E8 37 9F BC 76";

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.print("connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Use WiFiClientSecure class to create TLS connection
  WiFiClientSecure client;
  Serial.print("connecting to ");
  Serial.println(host);

  Serial.printf("Using fingerprint '%s'\n", fingerprint);
  client.setFingerprint(fingerprint);

  if (!client.connect(host, httpsPort)) {
    Serial.println("connection failed");
    return;
  }

  String url = "/repos/esp8266/Arduino/commits/master/status";
  Serial.print("requesting URL: ");
  Serial.println(url);

  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "User-Agent: BuildFailureDetectorESP8266\r\n" +
               "Connection: close\r\n\r\n");

  Serial.println("request sent");
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
      break;
    }
  }
  String line = client.readStringUntil('\n');
  if (line.startsWith("{\"state\":\"success\"")) {
    Serial.println("esp8266/Arduino CI successfull!");
  } else {
    Serial.println("esp8266/Arduino CI has failed");
  }
  Serial.println("reply was:");
  Serial.println("==========");
  Serial.println(line);
  Serial.println("==========");
  Serial.println("closing connection");
}

void loop() {
}

zoomkat:
There is a significant difference difference between http and https. The security part adds a lot of overhead. If you have a wifi board, you can look at the examples. Below is just some wifi example code that apparently can handle https operations.

Thanks, the problem was really with ssl communication. The code which you posted can be used but for other arduino boards - Ive got Arduino Uno Wifi rev2. But finaly I’ve found solution with wifinina.h library. Communication is working - I’m getting whole response from server.
But what I need is to have just response code and response body. After lots of googling I didn’t find solution. Is there solution from getting just the response code => 200/400 etc and response body from serial communication ? Because this part of code will print whole response with header.

  while (client.available()) {
    char c = client.read();
    Serial.write(c);
  }
#include <SPI.h>
#include <WiFiNINA.h>

//#include "arduino_secrets.h"
char ssid[] = "SSID_SECRET";
char pass[] = "PASS_SECRET";
int keyIndex = 0;            // your network key Index number (needed only for WEP)

int status = WL_IDLE_STATUS;
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
//IPAddress server(74,125,232,128);  // numeric IP for Google (no DNS)
char server[] = "www.schedy.sk";    // name address for Google (using DNS)

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
WiFiSSLClient client;

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to WiFi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to wifi");
  printWiFiStatus();

  Serial.println("\nStarting connection to server...");
  // if you get a connection, report back via serial:
  if (client.connect(server, 443)) {
    Serial.println("connected to server");
    // Make a HTTP request:
    client.println("POST /api/rfids HTTP/1.1");
    client.println("Host: www.schedy.sk");
    client.println("Connection: close");
    client.println();
  }
}

void loop() {
  // if there are incoming bytes available
  // from the server, read them and print them:
  while (client.available()) {
    char c = client.read();
    Serial.write(c);
  }

    
  // if the server's disconnected, stop the client:
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting from server.");
    client.stop();

    // do nothing forevermore:
    while (true);
  }
}


void printWiFiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

Response:

Attempting to connect to SSID: ///////
Connected to wifi
SSID: //////
IP Address: 192.168.100.19
signal strength (RSSI):-63 dBm

Starting connection to server...
connected to server
HTTP/1.1 200 OK
Date: Wed, 22 Jan 2020 17:39:53 GMT
Server: Apache/2.4.18 (Ubuntu)
Cache-Control: no-cache, private
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
Content-Length: 10
Connection: close
Content-Type: text/html; charset=UTF-8


post test
disconnecting from server.

“But what I need is to have just response code and response body.”

I assume what you are looking for only the “HTTP/1.1 200 OK” line where the sent data would be located. I think that would typically that would be the first line sent from the client. In some old server code snippet below, just that line is captured by stopping the capture at the first line feed “if (c == ‘\n’)” and the rest ignored.

  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 
          if(readString.indexOf('?') >=0) { //don't send new page
            client.println("HTTP/1.1 204 Zoomkat\r\n\r\n");
          }
          else {
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();