// AsyncJson.h /* Async Response to use with ArduinoJson and AsyncWebServer Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon. Example of callback in use server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) { AsyncJsonResponse * response = new AsyncJsonResponse(); JsonObject& root = response->getRoot(); root["key1"] = "key number one"; JsonObject& nested = root.createNestedObject("nested"); nested["key1"] = "key number one"; response->setLength(); request->send(response); }); -------------------- Async Request to use with ArduinoJson and AsyncWebServer Written by Arsène von Wyss (avonwyss) Example AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint"); handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) { JsonObject& jsonObj = json.as(); // ... }); server.addHandler(handler); */ #ifndef ASYNC_JSON_H_ #define ASYNC_JSON_H_ #include #include #include #if ARDUINOJSON_VERSION_MAJOR == 5 #define ARDUINOJSON_5_COMPATIBILITY #else #ifndef DYNAMIC_JSON_DOCUMENT_SIZE #define DYNAMIC_JSON_DOCUMENT_SIZE 1024 #endif #endif constexpr const char* JSON_MIMETYPE = "application/json"; /* * Json Response * */ class ChunkPrint : public Print { private: uint8_t* _destination; size_t _to_skip; size_t _to_write; size_t _pos; public: ChunkPrint(uint8_t* destination, size_t from, size_t len) : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {} virtual ~ChunkPrint(){} size_t write(uint8_t c){ if (_to_skip > 0) { _to_skip--; return 1; } else if (_to_write > 0) { _to_write--; _destination[_pos++] = c; return 1; } return 0; } size_t write(const uint8_t *buffer, size_t size) { return this->Print::write(buffer, size); } }; class AsyncJsonResponse: public AsyncAbstractResponse { protected: #ifdef ARDUINOJSON_5_COMPATIBILITY DynamicJsonBuffer _jsonBuffer; #else DynamicJsonDocument _jsonBuffer; #endif JsonVariant _root; bool _isValid; public: #ifdef ARDUINOJSON_5_COMPATIBILITY AsyncJsonResponse(bool isArray=false): _isValid{false} { _code = 200; _contentType = JSON_MIMETYPE; if(isArray) _root = _jsonBuffer.createArray(); else _root = _jsonBuffer.createObject(); } #else AsyncJsonResponse(bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid{false} { _code = 200; _contentType = JSON_MIMETYPE; if(isArray) _root = _jsonBuffer.createNestedArray(); else _root = _jsonBuffer.createNestedObject(); } #endif ~AsyncJsonResponse() {} JsonVariant & getRoot() { return _root; } bool _sourceValid() const { return _isValid; } size_t setLength() { #ifdef ARDUINOJSON_5_COMPATIBILITY _contentLength = _root.measureLength(); #else _contentLength = measureJson(_root); #endif if (_contentLength) { _isValid = true; } return _contentLength; } size_t getSize() { return _jsonBuffer.size(); } size_t _fillBuffer(uint8_t *data, size_t len){ ChunkPrint dest(data, _sentLength, len); #ifdef ARDUINOJSON_5_COMPATIBILITY _root.printTo( dest ) ; #else serializeJson(_root, dest); #endif return len; } }; class PrettyAsyncJsonResponse: public AsyncJsonResponse { public: #ifdef ARDUINOJSON_5_COMPATIBILITY PrettyAsyncJsonResponse (bool isArray=false) : AsyncJsonResponse{isArray} {} #else PrettyAsyncJsonResponse (bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} {} #endif size_t setLength () { #ifdef ARDUINOJSON_5_COMPATIBILITY _contentLength = _root.measurePrettyLength (); #else _contentLength = measureJsonPretty(_root); #endif if (_contentLength) {_isValid = true;} return _contentLength; } size_t _fillBuffer (uint8_t *data, size_t len) { ChunkPrint dest (data, _sentLength, len); #ifdef ARDUINOJSON_5_COMPATIBILITY _root.prettyPrintTo (dest); #else serializeJsonPretty(_root, dest); #endif return len; } }; typedef std::function ArJsonRequestHandlerFunction; class AsyncCallbackJsonWebHandler: public AsyncWebHandler { private: protected: const String _uri; WebRequestMethodComposite _method; ArJsonRequestHandlerFunction _onRequest; size_t _contentLength; #ifndef ARDUINOJSON_5_COMPATIBILITY const size_t maxJsonBufferSize; #endif size_t _maxContentLength; public: #ifdef ARDUINOJSON_5_COMPATIBILITY AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} #else AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMIC_JSON_DOCUMENT_SIZE) : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {} #endif void setMethod(WebRequestMethodComposite method){ _method = method; } void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; } void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; } virtual bool canHandle(AsyncWebServerRequest *request) override final{ if(!_onRequest) return false; if(!(_method & request->method())) return false; if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/"))) return false; if ( !request->contentType().equalsIgnoreCase(JSON_MIMETYPE) ) return false; request->addInterestingHeader("ANY"); return true; } virtual void handleRequest(AsyncWebServerRequest *request) override final { if(_onRequest) { if (request->_tempObject != NULL) { #ifdef ARDUINOJSON_5_COMPATIBILITY DynamicJsonBuffer jsonBuffer; JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject)); if (json.success()) { #else DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize); DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); if(!error) { JsonVariant json = jsonBuffer.as(); #endif _onRequest(request, json); return; } } request->send(_contentLength > _maxContentLength ? 413 : 400); } else { request->send(500); } } virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final { } virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { if (_onRequest) { _contentLength = total; if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { request->_tempObject = malloc(total); } if (request->_tempObject != NULL) { memcpy((uint8_t*)(request->_tempObject) + index, data, len); } } } virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;} }; #endif