132 lines
3.6 KiB
C++
132 lines
3.6 KiB
C++
#ifndef Async_Json_Request_Web_Handler_H_
|
|
#define Async_Json_Request_Web_Handler_H_
|
|
|
|
#include <ArduinoJson.h>
|
|
#include <ESPAsyncWebServer.h>
|
|
|
|
#define ASYNC_JSON_REQUEST_DEFAULT_MAX_SIZE 1024
|
|
#define ASYNC_JSON_REQUEST_MIMETYPE "application/json"
|
|
|
|
/*
|
|
* Handy little utility for dealing with small JSON request body payloads.
|
|
*
|
|
* Need to be careful using this as we are somewhat limited by RAM.
|
|
*
|
|
* Really only of use where there is a determinate payload size.
|
|
*/
|
|
|
|
typedef std::function<void(AsyncWebServerRequest* request, JsonDocument& jsonDocument)> JsonRequestCallback;
|
|
|
|
class AsyncJsonWebHandler : public AsyncWebHandler {
|
|
private:
|
|
WebRequestMethodComposite _method;
|
|
JsonRequestCallback _onRequest;
|
|
size_t _maxContentLength;
|
|
|
|
protected:
|
|
String _uri;
|
|
|
|
public:
|
|
AsyncJsonWebHandler() :
|
|
_method(HTTP_POST | HTTP_PUT | HTTP_PATCH),
|
|
_onRequest(nullptr),
|
|
_maxContentLength(ASYNC_JSON_REQUEST_DEFAULT_MAX_SIZE),
|
|
_uri() {
|
|
}
|
|
|
|
~AsyncJsonWebHandler() {
|
|
}
|
|
|
|
void setUri(const String& uri) {
|
|
_uri = uri;
|
|
}
|
|
void setMethod(WebRequestMethodComposite method) {
|
|
_method = method;
|
|
}
|
|
void setMaxContentLength(size_t maxContentLength) {
|
|
_maxContentLength = maxContentLength;
|
|
}
|
|
void onRequest(JsonRequestCallback 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(ASYNC_JSON_REQUEST_MIMETYPE))
|
|
return false;
|
|
|
|
request->addInterestingHeader("ANY");
|
|
return true;
|
|
}
|
|
|
|
virtual void handleRequest(AsyncWebServerRequest* request) override final {
|
|
// no request configured
|
|
if (!_onRequest) {
|
|
Serial.print("No request callback was configured for endpoint: ");
|
|
Serial.println(_uri);
|
|
request->send(500);
|
|
return;
|
|
}
|
|
|
|
// we have been handed too much data, return a 413 (payload too large)
|
|
if (request->contentLength() > _maxContentLength) {
|
|
request->send(413);
|
|
return;
|
|
}
|
|
|
|
// parse JSON and if possible handle the request
|
|
if (request->_tempObject) {
|
|
DynamicJsonDocument jsonDocument(_maxContentLength);
|
|
DeserializationError error = deserializeJson(jsonDocument, (uint8_t*)request->_tempObject);
|
|
if (error == DeserializationError::Ok) {
|
|
_onRequest(request, jsonDocument);
|
|
} else {
|
|
request->send(400);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// fallthrough, we have a null pointer, return 500.
|
|
// this can be due to running out of memory or never receiving body data.
|
|
request->send(500);
|
|
}
|
|
|
|
virtual void handleBody(AsyncWebServerRequest* request,
|
|
uint8_t* data,
|
|
size_t len,
|
|
size_t index,
|
|
size_t total) override final {
|
|
if (_onRequest) {
|
|
// don't allocate if data is too large
|
|
if (total > _maxContentLength) {
|
|
return;
|
|
}
|
|
|
|
// try to allocate memory on first call
|
|
// NB: the memory allocated here is freed by ~AsyncWebServerRequest
|
|
if (index == 0 && !request->_tempObject) {
|
|
request->_tempObject = malloc(total);
|
|
}
|
|
|
|
// copy the data into the buffer, if we have a buffer!
|
|
if (request->_tempObject) {
|
|
memcpy((uint8_t*)request->_tempObject + index, data, len);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual bool isRequestHandlerTrivial() override final {
|
|
return _onRequest ? false : true;
|
|
}
|
|
};
|
|
|
|
#endif // end Async_Json_Request_Web_Handler_H_
|