mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Refactor WebServer request handling for improved maintainability (#9470)
This commit is contained in:
		| @@ -1711,162 +1711,161 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| bool WebServer::canHandle(AsyncWebServerRequest *request) const { | 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; |     return true; | ||||||
|  |  | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
|   if (request->url() == "/events") { |   if (url == "/events") | ||||||
|     return true; |     return true; | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_WEBSERVER_CSS_INCLUDE | #ifdef USE_WEBSERVER_CSS_INCLUDE | ||||||
|   if (request->url() == "/0.css") |   if (url == "/0.css") | ||||||
|     return true; |     return true; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_WEBSERVER_JS_INCLUDE | #ifdef USE_WEBSERVER_JS_INCLUDE | ||||||
|   if (request->url() == "/0.js") |   if (url == "/0.js") | ||||||
|     return true; |     return true; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS | #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; |     return true; | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   // Store the URL to prevent temporary string destruction |   // Parse URL for component checks | ||||||
|   // 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(); |  | ||||||
|   UrlMatch match = match_url(url.c_str(), url.length(), true); |   UrlMatch match = match_url(url.c_str(), url.length(), true); | ||||||
|   if (!match.valid) |   if (!match.valid) | ||||||
|     return false; |     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 | #ifdef USE_SENSOR | ||||||
|   if (request->method() == HTTP_GET && match.domain_equals("sensor")) |     if (match.domain_equals("sensor")) | ||||||
|       return true; |       return true; | ||||||
| #endif | #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 | #ifdef USE_BINARY_SENSOR | ||||||
|   if (request->method() == HTTP_GET && match.domain_equals("binary_sensor")) |     if (match.domain_equals("binary_sensor")) | ||||||
|       return true; |       return true; | ||||||
| #endif | #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 | #ifdef USE_TEXT_SENSOR | ||||||
|   if (request->method() == HTTP_GET && match.domain_equals("text_sensor")) |     if (match.domain_equals("text_sensor")) | ||||||
|       return true; |       return true; | ||||||
| #endif | #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 | #ifdef USE_EVENT | ||||||
|   if (request->method() == HTTP_GET && match.domain_equals("event")) |     if (match.domain_equals("event")) | ||||||
|       return true; |       return true; | ||||||
| #endif | #endif | ||||||
|  |   } | ||||||
|  |  | ||||||
| #ifdef USE_UPDATE |   // GET+POST components | ||||||
|   if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("update")) |   if (is_get_or_post) { | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |     if (match.domain_equals("switch")) | ||||||
|       return true; |       return true; | ||||||
| #endif | #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; |   return false; | ||||||
| } | } | ||||||
| void WebServer::handleRequest(AsyncWebServerRequest *request) { | void WebServer::handleRequest(AsyncWebServerRequest *request) { | ||||||
|   if (request->url() == "/") { |   const auto &url = request->url(); | ||||||
|  |  | ||||||
|  |   // Handle static routes first | ||||||
|  |   if (url == "/") { | ||||||
|     this->handle_index_request(request); |     this->handle_index_request(request); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
|   if (request->url() == "/events") { |   if (url == "/events") { | ||||||
|     this->events_.add_new_client(this, request); |     this->events_.add_new_client(this, request); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_WEBSERVER_CSS_INCLUDE | #ifdef USE_WEBSERVER_CSS_INCLUDE | ||||||
|   if (request->url() == "/0.css") { |   if (url == "/0.css") { | ||||||
|     this->handle_css_request(request); |     this->handle_css_request(request); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_WEBSERVER_JS_INCLUDE | #ifdef USE_WEBSERVER_JS_INCLUDE | ||||||
|   if (request->url() == "/0.js") { |   if (url == "/0.js") { | ||||||
|     this->handle_js_request(request); |     this->handle_js_request(request); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -1879,147 +1878,85 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { | |||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   // See comment in canHandle() for why we store the URL reference |   // Parse URL for component routing | ||||||
|   const auto &url = request->url(); |  | ||||||
|   UrlMatch match = match_url(url.c_str(), url.length(), false); |   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 | #ifdef USE_SENSOR | ||||||
|   if (match.domain_equals("sensor")) { |       {"sensor", &WebServer::handle_sensor_request}, | ||||||
|     this->handle_sensor_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_SWITCH | #ifdef USE_SWITCH | ||||||
|   if (match.domain_equals("switch")) { |       {"switch", &WebServer::handle_switch_request}, | ||||||
|     this->handle_switch_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_BUTTON | #ifdef USE_BUTTON | ||||||
|   if (match.domain_equals("button")) { |       {"button", &WebServer::handle_button_request}, | ||||||
|     this->handle_button_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_BINARY_SENSOR | #ifdef USE_BINARY_SENSOR | ||||||
|   if (match.domain_equals("binary_sensor")) { |       {"binary_sensor", &WebServer::handle_binary_sensor_request}, | ||||||
|     this->handle_binary_sensor_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_FAN | #ifdef USE_FAN | ||||||
|   if (match.domain_equals("fan")) { |       {"fan", &WebServer::handle_fan_request}, | ||||||
|     this->handle_fan_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_LIGHT | #ifdef USE_LIGHT | ||||||
|   if (match.domain_equals("light")) { |       {"light", &WebServer::handle_light_request}, | ||||||
|     this->handle_light_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_TEXT_SENSOR | #ifdef USE_TEXT_SENSOR | ||||||
|   if (match.domain_equals("text_sensor")) { |       {"text_sensor", &WebServer::handle_text_sensor_request}, | ||||||
|     this->handle_text_sensor_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_COVER | #ifdef USE_COVER | ||||||
|   if (match.domain_equals("cover")) { |       {"cover", &WebServer::handle_cover_request}, | ||||||
|     this->handle_cover_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
|   if (match.domain_equals("number")) { |       {"number", &WebServer::handle_number_request}, | ||||||
|     this->handle_number_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_DATETIME_DATE | #ifdef USE_DATETIME_DATE | ||||||
|   if (match.domain_equals("date")) { |       {"date", &WebServer::handle_date_request}, | ||||||
|     this->handle_date_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_DATETIME_TIME | #ifdef USE_DATETIME_TIME | ||||||
|   if (match.domain_equals("time")) { |       {"time", &WebServer::handle_time_request}, | ||||||
|     this->handle_time_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_DATETIME_DATETIME | #ifdef USE_DATETIME_DATETIME | ||||||
|   if (match.domain_equals("datetime")) { |       {"datetime", &WebServer::handle_datetime_request}, | ||||||
|     this->handle_datetime_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_TEXT | #ifdef USE_TEXT | ||||||
|   if (match.domain_equals("text")) { |       {"text", &WebServer::handle_text_request}, | ||||||
|     this->handle_text_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|   if (match.domain_equals("select")) { |       {"select", &WebServer::handle_select_request}, | ||||||
|     this->handle_select_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_CLIMATE | #ifdef USE_CLIMATE | ||||||
|   if (match.domain_equals("climate")) { |       {"climate", &WebServer::handle_climate_request}, | ||||||
|     this->handle_climate_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_LOCK | #ifdef USE_LOCK | ||||||
|   if (match.domain_equals("lock")) { |       {"lock", &WebServer::handle_lock_request}, | ||||||
|     this->handle_lock_request(request, match); |  | ||||||
|  |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_VALVE | #ifdef USE_VALVE | ||||||
|   if (match.domain_equals("valve")) { |       {"valve", &WebServer::handle_valve_request}, | ||||||
|     this->handle_valve_request(request, match); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_ALARM_CONTROL_PANEL | #ifdef USE_ALARM_CONTROL_PANEL | ||||||
|   if (match.domain_equals("alarm_control_panel")) { |       {"alarm_control_panel", &WebServer::handle_alarm_control_panel_request}, | ||||||
|     this->handle_alarm_control_panel_request(request, match); |  | ||||||
|  |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_UPDATE | #ifdef USE_UPDATE | ||||||
|   if (match.domain_equals("update")) { |       {"update", &WebServer::handle_update_request}, | ||||||
|     this->handle_update_request(request, match); | #endif | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   // Check each route | ||||||
|  |   for (const auto &route : routes) { | ||||||
|  |     if (match.domain_equals(route.domain)) { | ||||||
|  |       (this->*route.handler)(request, match); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| #endif |   } | ||||||
|  |  | ||||||
|   // No matching handler found - send 404 |   // 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"); |   request->send(404, "text/plain", "Not Found"); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user