Consuming JSON-RPC 2.0 Web Services¶
ESP32-HTTP-Client provides native, zero-heap streaming support for JSON-RPC 2.0 over HTTP and HTTPS. It automatically serializes JSON-RPC 2.0 request envelopes, supports positional and named parameters, handles notifications, and extracts result payloads directly into C++ variables and mapped structs.
1. Positional Parameters (Array Params)¶
In JSON-RPC 2.0, when parameters are ordered as an array ("params": [15, 27]), use .param(value) or .addParam(value):
#include <Arduino.h>
#include <WiFi.h>
#include "ESP32HTTPClient.h"
ESP32HTTPClient client("https://api.example.com");
void setup() {
Serial.begin(115200);
WiFi.begin("SSID", "PASS");
while (WiFi.status() != WL_CONNECTED) delay(500);
int sum = 0;
// Sends: {"jsonrpc":"2.0","method":"add","params":[15,27],"id":1}
client.jsonRpc("/rpc")
.method("add")
.param(15)
.param(27)
.id(1)
.getResult(&sum);
if (client.isSuccess()) {
Serial.printf("Sum result: %d\n", sum);
}
}
void loop() {}
2. Named Parameters (Object Params)¶
When the server expects named parameters ("params": {"minuend": 42, "subtrahend": 23}), use .param(key, value) or .setParam(key, value):
int subResult = 0;
// Sends: {"jsonrpc":"2.0","method":"subtract","params":{"minuend":42,"subtrahend":23},"id":"sub-1"}
client.jsonRpc("/rpc")
.method("subtract")
.param("minuend", 42)
.param("subtrahend", 23)
.id("sub-1")
.getResult(&subResult);
Serial.printf("Subtraction result: %d\n", subResult);
3. Notifications (Fire-and-Forget)¶
Per the JSON-RPC 2.0 specification, a Notification is a request without an id member. The client does not expect or wait to parse a response:
// Sends: {"jsonrpc":"2.0","method":"logTelemetry","params":{"node":"esp32","uptime":120000}}
client.jsonRpc("/rpc")
.method("logTelemetry")
.asNotification()
.param("node", "esp32")
.param("uptime", 120000);
4. Struct Mapping with REST_JSON_MAP¶
You can pass C++ structs directly into JSON-RPC parameters or bind the result object directly into a struct without dynamic JSON documents:
struct SensorData {
char sensor[32] = {0};
float temperature = 0.0f;
float humidity = 0.0f;
REST_JSON_MAP(
REST_FIELD(sensor),
REST_FIELD(temperature),
REST_FIELD(humidity)
)
};
// 1. Send struct as params object:
SensorData report = {"DHT22", 24.5f, 55.0f};
client.jsonRpc("/rpc")
.method("saveReading")
.setParams(report)
.id(101);
// 2. Deserializing result into a struct:
SensorData latestReading;
client.jsonRpc("/rpc")
.method("getLatestReading")
.param("sensorId", 1)
.id(102)
.getResult(&latestReading);
Serial.printf("Sensor: %s, Temp: %.1f C\n", latestReading.sensor, latestReading.temperature);
5. Error Handling and Standard Error Codes¶
JSON-RPC 2.0 returns errors inside the error object ({"code": -32601, "message": "Method not found"}). The library provides standard error code constants and error inspection:
JsonRpcError rpcError;
int output = 0;
client.jsonRpc("/rpc")
.method("nonExistentMethod")
.id(5)
.getError(&rpcError)
.onJsonRpcError([](const JsonRpcError& err) {
Serial.printf("JSON-RPC Error [%d]: %s\n", err.code, err.message.c_str());
})
.getResult(&output);
if (rpcError.code == JSONRPC_ERR_METHOD_NOT_FOUND) {
Serial.println("Requested method was not found on server.");
}
Standard JSON-RPC Error Codes¶
| Constant | Code | Meaning |
|---|---|---|
JSONRPC_ERR_PARSE_ERROR |
-32700 |
Invalid JSON was received by the server. |
JSONRPC_ERR_INVALID_REQUEST |
-32600 |
The JSON sent is not a valid Request object. |
JSONRPC_ERR_METHOD_NOT_FOUND |
-32601 |
The method does not exist or is not available. |
JSONRPC_ERR_INVALID_PARAMS |
-32602 |
Invalid method parameter(s). |
JSONRPC_ERR_INTERNAL_ERROR |
-32603 |
Internal JSON-RPC error on server. |
JSONRPC_ERR_SERVER_ERROR_START to END |
-32099 to -32000 |
Reserved for implementation-defined server errors. |
6. Extracting Nested Fields and Raw JSON¶
Use dot notation to bind nested properties of the result object, or capture the raw JSON string:
char city[64] = {0};
String rawResult;
String rawHttpBody;
client.jsonRpc("/rpc")
.method("getUser")
.param("id", 42)
.id("u42")
.getResult("address.city", city, sizeof(city)) // Extract nested result field
.getRawResult(&rawResult) // Extract entire "result" object/array as raw JSON
.getRawResponse(&rawHttpBody); // Extract complete HTTP response body
7. Full Working Sketch¶
#include <Arduino.h>
#include <WiFi.h>
#include "ESP32HTTPClient.h"
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
ESP32HTTPClient client("https://api.example.com");
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi Connected!");
// Optional: Bearer authentication or custom headers
client.bearer("your_api_token");
// 1. Call with positional parameters
int sum = 0;
client.jsonRpc("/rpc")
.method("add")
.param(15)
.param(27)
.id(1)
.getResult(&sum);
Serial.printf("add(15, 27) = %d\n", sum);
// 2. Call with named parameters and error handling
int diff = 0;
client.jsonRpc("/rpc")
.method("subtract")
.param("minuend", 50)
.param("subtrahend", 12)
.id("sub-1")
.getResult(&diff)
.onJsonRpcError([](const JsonRpcError& err) {
Serial.printf("Error [%d]: %s\n", err.code, err.message.c_str());
});
Serial.printf("subtract(50, 12) = %d\n", diff);
// 3. Notification (no id, fire-and-forget)
client.jsonRpc("/rpc")
.method("heartbeat")
.asNotification()
.param("node", "esp32");
}
void loop() {}