You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
7.3 KiB
254 lines
7.3 KiB
// 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<JsonObject>(); |
|
// ... |
|
}); |
|
server.addHandler(handler); |
|
|
|
*/ |
|
#ifndef ASYNC_JSON_H_ |
|
#define ASYNC_JSON_H_ |
|
#include <ArduinoJson.h> |
|
#include <ESPAsyncWebServer.h> |
|
#include <Print.h> |
|
|
|
#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<void(AsyncWebServerRequest *request, JsonVariant &json)> 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<JsonVariant>(); |
|
#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
|
|
|