mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
Refactor WebServer request handling for improved maintainability (#9470)
This commit is contained in:
parent
e231d334a3
commit
f8c45573f3
@ -1711,162 +1711,161 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c
|
||||
#endif
|
||||
|
||||
bool WebServer::canHandle(AsyncWebServerRequest *request) const {
|
||||
if (request->url() == "/")
|
||||
const auto &url = request->url();
|
||||
const auto method = request->method();
|
||||
|
||||
// Simple URL checks
|
||||
if (url == "/")
|
||||
return true;
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
if (request->url() == "/events") {
|
||||
if (url == "/events")
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_CSS_INCLUDE
|
||||
if (request->url() == "/0.css")
|
||||
if (url == "/0.css")
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_JS_INCLUDE
|
||||
if (request->url() == "/0.js")
|
||||
if (url == "/0.js")
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
|
||||
if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
|
||||
if (method == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Store the URL to prevent temporary string destruction
|
||||
// request->url() returns a reference to a String (on Arduino) or std::string (on ESP-IDF)
|
||||
// UrlMatch stores pointers to the string's data, so we must ensure the string outlives match_url()
|
||||
const auto &url = request->url();
|
||||
// Parse URL for component checks
|
||||
UrlMatch match = match_url(url.c_str(), url.length(), true);
|
||||
if (!match.valid)
|
||||
return false;
|
||||
|
||||
// Common pattern check
|
||||
bool is_get = method == HTTP_GET;
|
||||
bool is_post = method == HTTP_POST;
|
||||
bool is_get_or_post = is_get || is_post;
|
||||
|
||||
if (!is_get_or_post)
|
||||
return false;
|
||||
|
||||
// GET-only components
|
||||
if (is_get) {
|
||||
#ifdef USE_SENSOR
|
||||
if (request->method() == HTTP_GET && match.domain_equals("sensor"))
|
||||
return true;
|
||||
if (match.domain_equals("sensor"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("switch"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_BUTTON
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("button"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (request->method() == HTTP_GET && match.domain_equals("binary_sensor"))
|
||||
return true;
|
||||
if (match.domain_equals("binary_sensor"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_FAN
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("fan"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("light"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (request->method() == HTTP_GET && match.domain_equals("text_sensor"))
|
||||
return true;
|
||||
if (match.domain_equals("text_sensor"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_COVER
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("cover"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("number"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("date"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("time"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("datetime"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("text"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("select"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("climate"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_LOCK
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("lock"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_VALVE
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("valve"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
if ((request->method() == HTTP_GET || request->method() == HTTP_POST) && match.domain_equals("alarm_control_panel"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
if (request->method() == HTTP_GET && match.domain_equals("event"))
|
||||
return true;
|
||||
if (match.domain_equals("event"))
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_UPDATE
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("update"))
|
||||
return true;
|
||||
// GET+POST components
|
||||
if (is_get_or_post) {
|
||||
#ifdef USE_SWITCH
|
||||
if (match.domain_equals("switch"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
if (match.domain_equals("button"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
if (match.domain_equals("fan"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
if (match.domain_equals("light"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
if (match.domain_equals("cover"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
if (match.domain_equals("number"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
if (match.domain_equals("date"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
if (match.domain_equals("time"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
if (match.domain_equals("datetime"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
if (match.domain_equals("text"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
if (match.domain_equals("select"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
if (match.domain_equals("climate"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
if (match.domain_equals("lock"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
if (match.domain_equals("valve"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
if (match.domain_equals("alarm_control_panel"))
|
||||
return true;
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
if (match.domain_equals("update"))
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
||||
if (request->url() == "/") {
|
||||
const auto &url = request->url();
|
||||
|
||||
// Handle static routes first
|
||||
if (url == "/") {
|
||||
this->handle_index_request(request);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
if (request->url() == "/events") {
|
||||
if (url == "/events") {
|
||||
this->events_.add_new_client(this, request);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_CSS_INCLUDE
|
||||
if (request->url() == "/0.css") {
|
||||
if (url == "/0.css") {
|
||||
this->handle_css_request(request);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER_JS_INCLUDE
|
||||
if (request->url() == "/0.js") {
|
||||
if (url == "/0.js") {
|
||||
this->handle_js_request(request);
|
||||
return;
|
||||
}
|
||||
@ -1879,147 +1878,85 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// See comment in canHandle() for why we store the URL reference
|
||||
const auto &url = request->url();
|
||||
// Parse URL for component routing
|
||||
UrlMatch match = match_url(url.c_str(), url.length(), false);
|
||||
|
||||
// Component routing using minimal code repetition
|
||||
struct ComponentRoute {
|
||||
const char *domain;
|
||||
void (WebServer::*handler)(AsyncWebServerRequest *, const UrlMatch &);
|
||||
};
|
||||
|
||||
static const ComponentRoute routes[] = {
|
||||
#ifdef USE_SENSOR
|
||||
if (match.domain_equals("sensor")) {
|
||||
this->handle_sensor_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"sensor", &WebServer::handle_sensor_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
if (match.domain_equals("switch")) {
|
||||
this->handle_switch_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"switch", &WebServer::handle_switch_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_BUTTON
|
||||
if (match.domain_equals("button")) {
|
||||
this->handle_button_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"button", &WebServer::handle_button_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (match.domain_equals("binary_sensor")) {
|
||||
this->handle_binary_sensor_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"binary_sensor", &WebServer::handle_binary_sensor_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_FAN
|
||||
if (match.domain_equals("fan")) {
|
||||
this->handle_fan_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"fan", &WebServer::handle_fan_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
if (match.domain_equals("light")) {
|
||||
this->handle_light_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"light", &WebServer::handle_light_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (match.domain_equals("text_sensor")) {
|
||||
this->handle_text_sensor_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"text_sensor", &WebServer::handle_text_sensor_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_COVER
|
||||
if (match.domain_equals("cover")) {
|
||||
this->handle_cover_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"cover", &WebServer::handle_cover_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
if (match.domain_equals("number")) {
|
||||
this->handle_number_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"number", &WebServer::handle_number_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
if (match.domain_equals("date")) {
|
||||
this->handle_date_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"date", &WebServer::handle_date_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
if (match.domain_equals("time")) {
|
||||
this->handle_time_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"time", &WebServer::handle_time_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
if (match.domain_equals("datetime")) {
|
||||
this->handle_datetime_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"datetime", &WebServer::handle_datetime_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
if (match.domain_equals("text")) {
|
||||
this->handle_text_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"text", &WebServer::handle_text_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
if (match.domain_equals("select")) {
|
||||
this->handle_select_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"select", &WebServer::handle_select_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
if (match.domain_equals("climate")) {
|
||||
this->handle_climate_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"climate", &WebServer::handle_climate_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_LOCK
|
||||
if (match.domain_equals("lock")) {
|
||||
this->handle_lock_request(request, match);
|
||||
|
||||
return;
|
||||
}
|
||||
{"lock", &WebServer::handle_lock_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_VALVE
|
||||
if (match.domain_equals("valve")) {
|
||||
this->handle_valve_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"valve", &WebServer::handle_valve_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
if (match.domain_equals("alarm_control_panel")) {
|
||||
this->handle_alarm_control_panel_request(request, match);
|
||||
|
||||
return;
|
||||
}
|
||||
{"alarm_control_panel", &WebServer::handle_alarm_control_panel_request},
|
||||
#endif
|
||||
|
||||
#ifdef USE_UPDATE
|
||||
if (match.domain_equals("update")) {
|
||||
this->handle_update_request(request, match);
|
||||
return;
|
||||
}
|
||||
{"update", &WebServer::handle_update_request},
|
||||
#endif
|
||||
};
|
||||
|
||||
// Check each route
|
||||
for (const auto &route : routes) {
|
||||
if (match.domain_equals(route.domain)) {
|
||||
(this->*route.handler)(request, match);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No matching handler found - send 404
|
||||
ESP_LOGV(TAG, "Request for unknown URL: %s", request->url().c_str());
|
||||
ESP_LOGV(TAG, "Request for unknown URL: %s", url.c_str());
|
||||
request->send(404, "text/plain", "Not Found");
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user