diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index adf8b22b..5b8638dd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,10 @@
 add_library(oatpp
         oatpp/algorithm/CRC.cpp
         oatpp/algorithm/CRC.hpp
+        oatpp/codegen/api_controller/base_define.hpp
+        oatpp/codegen/api_controller/base_undef.hpp
+        oatpp/codegen/api_controller/auth_define.hpp
+        oatpp/codegen/api_controller/auth_undef.hpp
         oatpp/codegen/codegen_define_ApiClient_.hpp
         oatpp/codegen/codegen_define_ApiController_.hpp
         oatpp/codegen/codegen_define_DTO_.hpp
diff --git a/src/oatpp/codegen/api_controller/auth_define.hpp b/src/oatpp/codegen/api_controller/auth_define.hpp
new file mode 100644
index 00000000..51fdbf3a
--- /dev/null
+++ b/src/oatpp/codegen/api_controller/auth_define.hpp
@@ -0,0 +1,67 @@
+/***************************************************************************
+ *
+ * Project         _____    __   ____   _      _
+ *                (  _  )  /__\ (_  _)_| |_  _| |_
+ *                 )(_)(  /(__)\  )( (_   _)(_   _)
+ *                (_____)(__)(__)(__)  |_|    |_|
+ *
+ *
+ * Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ***************************************************************************/
+
+#define AUTHORIZATION(TYPE, ...) \
+OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_AUTHORIZATION, OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO, TYPE, (__VA_ARGS__))
+
+// AUTHORIZATION MACRO // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_1(TYPE, NAME) \
+auto __param_str_val_##NAME = __request->getHeader(oatpp::web::protocol::http::Header::AUTHORIZATION); \
+std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = ApiController::handleDefaultAuthorization(__param_str_val_##NAME); \
+TYPE NAME = std::static_pointer_cast<TYPE::element_type>(__param_aosp_val_##NAME);
+
+#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_2(TYPE, NAME, AUTH_HANDLER) \
+auto __param_str_val_##NAME = __request->getHeader(oatpp::web::protocol::http::Header::AUTHORIZATION); \
+std::shared_ptr<oatpp::web::server::handler::AuthorizationHandler> __auth_handler_##NAME = AUTH_HANDLER; \
+std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = __auth_handler_##NAME->handleAuthorization(__param_str_val_##NAME); \
+TYPE NAME = std::static_pointer_cast<TYPE::element_type>(__param_aosp_val_##NAME);
+
+#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION(TYPE, PARAM_LIST) \
+OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
+
+// __INFO
+
+#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_1(TYPE, NAME) \
+auto __param_obj_##NAME = ApiController::getDefaultAuthorizationHandler(); \
+if(__param_obj_##NAME) { \
+  info->headers.add(oatpp::web::protocol::http::Header::AUTHORIZATION, oatpp::String::Class::getType()); \
+  info->headers[oatpp::web::protocol::http::Header::AUTHORIZATION].description = __param_obj_##NAME ->getScheme(); \
+  info->authorization = __param_obj_##NAME ->getScheme(); \
+} else { \
+  throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "No authorization handler set up in controller before controller was added to router or swagger-doc."); \
+}
+
+#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_2(TYPE, NAME, AUTH_HANDLER) \
+std::shared_ptr<oatpp::web::server::handler::AuthorizationHandler> __auth_handler_##NAME = AUTH_HANDLER; \
+if(__auth_handler_##NAME) { \
+  info->headers.add(oatpp::web::protocol::http::Header::AUTHORIZATION, oatpp::String::Class::getType()); \
+  info->headers[oatpp::web::protocol::http::Header::AUTHORIZATION].description = __auth_handler_##NAME->getScheme(); \
+  info->authorization = __auth_handler_##NAME->getScheme(); \
+} else { \
+  throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "Invalid authorization handler given (or not set up) in AUTHORIZATION(TYPE, NAME, AUTH_HANDLER) before controller was added to router or swagger-doc."); \
+}
+
+#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO(TYPE, PARAM_LIST) \
+OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
diff --git a/src/oatpp/codegen/api_controller/auth_undef.hpp b/src/oatpp/codegen/api_controller/auth_undef.hpp
new file mode 100644
index 00000000..f2984caa
--- /dev/null
+++ b/src/oatpp/codegen/api_controller/auth_undef.hpp
@@ -0,0 +1,35 @@
+/***************************************************************************
+ *
+ * Project         _____    __   ____   _      _
+ *                (  _  )  /__\ (_  _)_| |_  _| |_
+ *                 )(_)(  /(__)\  )( (_   _)(_   _)
+ *                (_____)(__)(__)(__)  |_|    |_|
+ *
+ *
+ * Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ***************************************************************************/
+
+#undef AUTHORIZATION
+
+// AUTHORIZATION MACRO // ------------------------------------------------------
+#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_1
+#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_2
+#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION
+
+// __INFO
+#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_1
+#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_2
+#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO
diff --git a/src/oatpp/codegen/api_controller/base_define.hpp b/src/oatpp/codegen/api_controller/base_define.hpp
new file mode 100644
index 00000000..d6edd490
--- /dev/null
+++ b/src/oatpp/codegen/api_controller/base_define.hpp
@@ -0,0 +1,522 @@
+/***************************************************************************
+ *
+ * Project         _____    __   ____   _      _
+ *                (  _  )  /__\ (_  _)_| |_  _| |_
+ *                 )(_)(  /(__)\  )( (_   _)(_   _)
+ *                (_____)(__)(__)(__)  |_|    |_|
+ *
+ *
+ * Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ***************************************************************************/
+
+#define OATPP_MACRO_API_CONTROLLER_PARAM_MACRO(MACRO, INFO, TYPE, PARAM_LIST) MACRO(TYPE, PARAM_LIST)
+#define OATPP_MACRO_API_CONTROLLER_PARAM_INFO(MACRO, INFO, TYPE, PARAM_LIST) INFO(TYPE, PARAM_LIST)
+#define OATPP_MACRO_API_CONTROLLER_PARAM_TYPE(MACRO, INFO, TYPE, PARAM_LIST) TYPE
+#define OATPP_MACRO_API_CONTROLLER_PARAM_NAME(MACRO, INFO, TYPE, PARAM_LIST) OATPP_MACRO_FIRSTARG PARAM_LIST
+#define OATPP_MACRO_API_CONTROLLER_PARAM_TYPE_STR(MACRO, INFO, TYPE, PARAM_LIST) #TYPE
+#define OATPP_MACRO_API_CONTROLLER_PARAM_NAME_STR(MACRO, INFO, TYPE, PARAM_LIST) OATPP_MACRO_FIRSTARG_STR PARAM_LIST
+#define OATPP_MACRO_API_CONTROLLER_PARAM(MACRO, INFO, TYPE, PARAM_LIST) (MACRO, INFO, TYPE, PARAM_LIST)
+
+#define REQUEST(TYPE, ...) \
+OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_REQUEST, OATPP_MACRO_API_CONTROLLER_REQUEST_INFO, TYPE, (__VA_ARGS__))
+
+#define HEADER(TYPE, ...) \
+OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_HEADER, OATPP_MACRO_API_CONTROLLER_HEADER_INFO, TYPE, (__VA_ARGS__))
+
+#define PATH(TYPE, ...) \
+OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_PATH, OATPP_MACRO_API_CONTROLLER_PATH_INFO, TYPE, (__VA_ARGS__))
+
+#define QUERIES(TYPE, ...) \
+OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_QUERIES, OATPP_MACRO_API_CONTROLLER_QUERIES_INFO, TYPE, (__VA_ARGS__))
+
+#define QUERY(TYPE, ...) \
+OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_QUERY, OATPP_MACRO_API_CONTROLLER_QUERY_INFO, TYPE, (__VA_ARGS__))
+
+#define BODY_STRING(TYPE, ...) \
+OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_BODY_STRING, OATPP_MACRO_API_CONTROLLER_BODY_STRING_INFO, TYPE, (__VA_ARGS__))
+
+#define BODY_DTO(TYPE, ...) \
+OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_BODY_DTO, OATPP_MACRO_API_CONTROLLER_BODY_DTO_INFO, TYPE, (__VA_ARGS__))
+
+//////////////////////////////////////////////////////////////////////////
+
+#define OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(MACRO, TYPE, ...) \
+OATPP_MACRO_EXPAND(OATPP_MACRO_MACRO_SELECTOR(MACRO, (__VA_ARGS__)) (TYPE, __VA_ARGS__))
+
+//////////////////////////////////////////////////////////////////////////
+
+// REQUEST MACRO // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_REQUEST(TYPE, PARAM_LIST) \
+TYPE OATPP_MACRO_FIRSTARG PARAM_LIST = __request;
+
+#define OATPP_MACRO_API_CONTROLLER_REQUEST_INFO(TYPE, PARAM_LIST)
+
+
+// HEADER MACRO // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_HEADER_1(TYPE, NAME) \
+auto __param_str_val_##NAME = __request->getHeader(#NAME); \
+if(!__param_str_val_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, "Missing HEADER parameter '" #NAME "'"); \
+} \
+bool __param_validation_check_##NAME; \
+TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
+if(!__param_validation_check_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, "Invalid HEADER parameter '" #NAME "'. Expected type is '" #TYPE "'"); \
+}
+
+#define OATPP_MACRO_API_CONTROLLER_HEADER_2(TYPE, NAME, QUALIFIER) \
+auto __param_str_val_##NAME = __request->getHeader(QUALIFIER); \
+if(!__param_str_val_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, \
+  oatpp::String("Missing HEADER parameter '") + QUALIFIER + "'"); \
+} \
+bool __param_validation_check_##NAME; \
+TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
+if(!__param_validation_check_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, \
+                                    oatpp::String("Invalid HEADER parameter '") + \
+                                    QUALIFIER + \
+                                    "'. Expected type is '" #TYPE "'"); \
+}
+
+#define OATPP_MACRO_API_CONTROLLER_HEADER(TYPE, PARAM_LIST) \
+OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_HEADER_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
+
+// __INFO
+
+#define OATPP_MACRO_API_CONTROLLER_HEADER_INFO_1(TYPE, NAME) \
+info->headers.add(#NAME, TYPE::Class::getType());
+
+#define OATPP_MACRO_API_CONTROLLER_HEADER_INFO_2(TYPE, NAME, QUALIFIER) \
+info->headers.add(QUALIFIER, TYPE::Class::getType());
+
+#define OATPP_MACRO_API_CONTROLLER_HEADER_INFO(TYPE, PARAM_LIST) \
+OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_HEADER_INFO_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
+
+
+// PATH MACRO // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_PATH_1(TYPE, NAME) \
+auto __param_str_val_##NAME = __request->getPathVariable(#NAME); \
+if(!__param_str_val_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, "Missing PATH parameter '" #NAME "'"); \
+} \
+bool __param_validation_check_##NAME; \
+TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
+if(!__param_validation_check_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, "Invalid PATH parameter '" #NAME "'. Expected type is '" #TYPE "'"); \
+}
+
+#define OATPP_MACRO_API_CONTROLLER_PATH_2(TYPE, NAME, QUALIFIER) \
+auto __param_str_val_##NAME = __request->getPathVariable(QUALIFIER); \
+if(!__param_str_val_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, \
+  oatpp::String("Missing PATH parameter '") + QUALIFIER + "'"); \
+} \
+bool __param_validation_check_##NAME; \
+TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
+if(!__param_validation_check_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, \
+                                    oatpp::String("Invalid PATH parameter '") + \
+                                    QUALIFIER + \
+                                    "'. Expected type is '" #TYPE "'"); \
+}
+
+#define OATPP_MACRO_API_CONTROLLER_PATH(TYPE, PARAM_LIST) \
+OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_PATH_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
+
+// __INFO
+
+#define OATPP_MACRO_API_CONTROLLER_PATH_INFO_1(TYPE, NAME) \
+info->pathParams.add(#NAME, TYPE::Class::getType());
+
+#define OATPP_MACRO_API_CONTROLLER_PATH_INFO_2(TYPE, NAME, QUALIFIER) \
+info->pathParams.add(QUALIFIER, TYPE::Class::getType());
+
+#define OATPP_MACRO_API_CONTROLLER_PATH_INFO(TYPE, PARAM_LIST) \
+OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_PATH_INFO_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
+
+// QUERIES MACRO // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_QUERIES(TYPE, PARAM_LIST) \
+TYPE OATPP_MACRO_FIRSTARG PARAM_LIST = __request->getQueryParameters();
+
+#define OATPP_MACRO_API_CONTROLLER_QUERIES_INFO(TYPE, PARAM_LIST)
+
+// QUERY MACRO // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_QUERY_1(TYPE, NAME) \
+auto __param_str_val_##NAME = __request->getQueryParameter(#NAME); \
+if(!__param_str_val_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, "Missing QUERY parameter '" #NAME "'"); \
+} \
+bool __param_validation_check_##NAME; \
+TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
+if(!__param_validation_check_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, "Invalid QUERY parameter '" #NAME "'. Expected type is '" #TYPE "'"); \
+}
+
+#define OATPP_MACRO_API_CONTROLLER_QUERY_2(TYPE, NAME, QUALIFIER) \
+auto __param_str_val_##NAME = __request->getQueryParameter(QUALIFIER); \
+if(!__param_str_val_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, \
+  oatpp::String("Missing QUERY parameter '") + QUALIFIER + "'"); \
+} \
+bool __param_validation_check_##NAME; \
+TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
+if(!__param_validation_check_##NAME){ \
+  return ApiController::handleError(Status::CODE_400, \
+                                    oatpp::String("Invalid QUERY parameter '") + \
+                                    QUALIFIER + \
+                                    "'. Expected type is '" #TYPE "'"); \
+}
+
+#define OATPP_MACRO_API_CONTROLLER_QUERY(TYPE, PARAM_LIST) \
+OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_QUERY_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
+
+// __INFO
+
+#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO_1(TYPE, NAME) \
+info->queryParams.add(#NAME, TYPE::Class::getType());
+
+#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO_2(TYPE, NAME, QUALIFIER) \
+info->queryParams.add(QUALIFIER, TYPE::Class::getType());
+
+#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO(TYPE, PARAM_LIST) \
+OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_QUERY_INFO_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
+
+// BODY_STRING MACRO // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_BODY_STRING(TYPE, PARAM_LIST) \
+TYPE OATPP_MACRO_FIRSTARG PARAM_LIST = __request->readBodyToString();
+
+// __INFO
+
+#define OATPP_MACRO_API_CONTROLLER_BODY_STRING_INFO(TYPE, PARAM_LIST) \
+info->body.name = OATPP_MACRO_FIRSTARG_STR PARAM_LIST; \
+info->body.type = oatpp::data::mapping::type::__class::String::getType();
+
+// BODY_DTO MACRO // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_BODY_DTO(TYPE, PARAM_LIST) \
+TYPE OATPP_MACRO_FIRSTARG PARAM_LIST; \
+__request->readBodyToDto(OATPP_MACRO_FIRSTARG PARAM_LIST, getDefaultObjectMapper().get()); \
+if(!OATPP_MACRO_FIRSTARG PARAM_LIST) { \
+  return ApiController::handleError(Status::CODE_400, "Missing valid body parameter '" OATPP_MACRO_FIRSTARG_STR PARAM_LIST "'"); \
+}
+
+// __INFO
+
+#define OATPP_MACRO_API_CONTROLLER_BODY_DTO_INFO(TYPE, PARAM_LIST) \
+info->body.name = OATPP_MACRO_FIRSTARG_STR PARAM_LIST; \
+info->body.type = TYPE::Class::getType();
+
+// FOR EACH // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL_FIRST(INDEX, COUNT, X) \
+OATPP_MACRO_API_CONTROLLER_PARAM_TYPE X OATPP_MACRO_API_CONTROLLER_PARAM_NAME X
+
+#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL_REST(INDEX, COUNT, X) \
+, OATPP_MACRO_API_CONTROLLER_PARAM_TYPE X OATPP_MACRO_API_CONTROLLER_PARAM_NAME X
+
+#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_PUT(INDEX, COUNT, X) \
+OATPP_MACRO_API_CONTROLLER_PARAM_MACRO X
+
+#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL_FIRST(INDEX, COUNT, X) \
+OATPP_MACRO_API_CONTROLLER_PARAM_NAME X
+
+#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL_REST(INDEX, COUNT, X) \
+, OATPP_MACRO_API_CONTROLLER_PARAM_NAME X
+
+#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_INFO(INDEX, COUNT, X) \
+OATPP_MACRO_API_CONTROLLER_PARAM_INFO X
+
+// ENDPOINT_INFO MACRO // ------------------------------------------------------
+
+#define ENDPOINT_INFO(NAME) \
+\
+std::shared_ptr<Endpoint::Info> Z__ENDPOINT_CREATE_ADDITIONAL_INFO_##NAME() { \
+  auto info = Z__EDNPOINT_INFO_GET_INSTANCE_##NAME(); \
+  Z__ENDPOINT_ADD_INFO_##NAME(info); \
+  return info; \
+} \
+\
+const std::shared_ptr<Endpoint::Info> Z__ENDPOINT_ADDITIONAL_INFO_##NAME = Z__ENDPOINT_CREATE_ADDITIONAL_INFO_##NAME(); \
+\
+void Z__ENDPOINT_ADD_INFO_##NAME(const std::shared_ptr<Endpoint::Info>& info)
+
+// ENDPOINT MACRO // ------------------------------------------------------
+
+#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_DEFAULTS(NAME, METHOD, PATH) \
+\
+template<class T> \
+static typename std::shared_ptr<Handler<T>> Z__ENDPOINT_HANDLER_GET_INSTANCE_##NAME(T* controller) { \
+  auto handler = std::static_pointer_cast<Handler<T>>(controller->getEndpointHandler(#NAME)); \
+  if(!handler) { \
+    handler = Handler<T>::createShared(controller, &T::Z__PROXY_METHOD_##NAME, nullptr); \
+    controller->setEndpointHandler(#NAME, handler); \
+  } \
+  return handler; \
+} \
+\
+std::shared_ptr<Endpoint::Info> Z__EDNPOINT_INFO_GET_INSTANCE_##NAME() { \
+  std::shared_ptr<Endpoint::Info> info = getEndpointInfo(#NAME); \
+  if(!info){ \
+    info = Endpoint::Info::createShared(); \
+    setEndpointInfo(#NAME, info); \
+  } \
+  return info; \
+}
+
+#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_0(NAME, METHOD, PATH)  \
+\
+EndpointInfoBuilder Z__CREATE_ENDPOINT_INFO_##NAME = [this](){ \
+  auto info = Z__EDNPOINT_INFO_GET_INSTANCE_##NAME(); \
+  info->name = #NAME; \
+  info->path = PATH; \
+  info->method = METHOD; \
+  return info; \
+}; \
+\
+const std::shared_ptr<Endpoint> Z__ENDPOINT_##NAME = createEndpoint(m_endpoints, \
+                                                        Z__ENDPOINT_HANDLER_GET_INSTANCE_##NAME(this), \
+                                                        Z__CREATE_ENDPOINT_INFO_##NAME);
+
+#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_0(NAME, METHOD, PATH) \
+OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_DEFAULTS(NAME, METHOD, PATH) \
+OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_0(NAME, METHOD, PATH) \
+\
+std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> \
+Z__PROXY_METHOD_##NAME(const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& __request) \
+{ \
+  (void)__request; \
+  return NAME(); \
+} \
+\
+std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> NAME()
+
+////////////////////
+
+#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_1(NAME, METHOD, PATH, ...)  \
+\
+EndpointInfoBuilder Z__CREATE_ENDPOINT_INFO_##NAME = [this](){ \
+auto info = Z__EDNPOINT_INFO_GET_INSTANCE_##NAME(); \
+  info->name = #NAME; \
+  info->path = PATH; \
+  info->method = METHOD; \
+  OATPP_MACRO_FOREACH(OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_INFO, __VA_ARGS__) \
+  return info; \
+}; \
+\
+const std::shared_ptr<Endpoint> Z__ENDPOINT_##NAME = createEndpoint(m_endpoints, \
+                                                        Z__ENDPOINT_HANDLER_GET_INSTANCE_##NAME(this), \
+                                                        Z__CREATE_ENDPOINT_INFO_##NAME);
+
+#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_1(NAME, METHOD, PATH, ...) \
+OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_DEFAULTS(NAME, METHOD, PATH) \
+OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_1(NAME, METHOD, PATH, __VA_ARGS__) \
+\
+std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> \
+Z__PROXY_METHOD_##NAME(const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& __request) \
+{ \
+  OATPP_MACRO_FOREACH(OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_PUT, __VA_ARGS__) \
+  return NAME( \
+    OATPP_MACRO_FOREACH_FIRST_AND_REST( \
+      OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL_FIRST, \
+      OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL_REST, \
+      __VA_ARGS__ \
+    ) \
+  ); \
+} \
+\
+std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> NAME(\
+  OATPP_MACRO_FOREACH_FIRST_AND_REST( \
+    OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL_FIRST, \
+    OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL_REST, \
+    __VA_ARGS__ \
+  ) \
+)
+
+// Chooser
+
+#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_0(METHOD, PATH, NAME) \
+OATPP_MACRO_EXPAND(OATPP_MACRO_API_CONTROLLER_ENDPOINT_0(NAME, METHOD, PATH))
+
+#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_1(METHOD, PATH, NAME, ...) \
+OATPP_MACRO_EXPAND(OATPP_MACRO_API_CONTROLLER_ENDPOINT_1(NAME, METHOD, PATH, __VA_ARGS__))
+
+/**
+ * Codegen macoro to be used in `oatpp::web::server::api::ApiController` to generate Endpoint.
+ * @param METHOD - Http method ("GET", "POST", "PUT", etc.).
+ * @param PATH - Path to endpoint (without host).
+ * @param NAME - Name of the generated method.
+ * @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;.
+ */
+#define ENDPOINT(METHOD, PATH, ...) \
+OATPP_MACRO_EXPAND(OATPP_MACRO_MACRO_BINARY_SELECTOR(OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_, (__VA_ARGS__)) (METHOD, PATH, __VA_ARGS__))
+
+/**
+ * Endpoint interceptor
+ */
+#define ENDPOINT_INTERCEPTOR(ENDPOINT_NAME, NAME) \
+\
+Handler<oatpp::web::server::api::ApiController>::Method \
+  Z__INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME = Z__INTERCEPTOR_METHOD_SET_##ENDPOINT_NAME ##_ ##NAME(this); \
+\
+template<class T> \
+Handler<oatpp::web::server::api::ApiController>::Method Z__INTERCEPTOR_METHOD_SET_##ENDPOINT_NAME ##_ ##NAME (T* controller) { \
+  return static_cast<Handler<oatpp::web::server::api::ApiController>::Method>( \
+    Z__ENDPOINT_HANDLER_GET_INSTANCE_##ENDPOINT_NAME(controller)->setMethod(&T::Z__PROXY_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME) \
+  ); \
+} \
+\
+std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> \
+Z__PROXY_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME(const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& request) { \
+  return Z__USER_PROXY_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME(this, request); \
+} \
+\
+template<class T> \
+std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> \
+Z__USER_PROXY_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME( \
+  T* controller, \
+  const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& request \
+) { \
+  auto intercepted = static_cast<typename Handler<T>::Method>(Z__INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME); \
+  return Z__USER_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME <T> (intercepted, request); \
+} \
+\
+template<class T> \
+std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> \
+Z__USER_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME( \
+  typename Handler<T>::Method intercepted, \
+  const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& request \
+)
+
+// ENDPOINT ASYNC MACRO // ------------------------------------------------------
+
+/*
+ *  1 - Method to obtain endpoint call function ptr
+ *  2 - Endpoint info singleton
+ */
+#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL_DEFAULTS(NAME, METHOD, PATH) \
+\
+template<class T> \
+static typename std::shared_ptr<Handler<T>> Z__ENDPOINT_HANDLER_GET_INSTANCE_##NAME(T* controller) { \
+  auto handler = std::static_pointer_cast<Handler<T>>(controller->getEndpointHandler(#NAME)); \
+  if(!handler) { \
+    handler = Handler<T>::createShared(controller, nullptr, &T::Z__PROXY_METHOD_##NAME); \
+    controller->setEndpointHandler(#NAME, handler); \
+  } \
+  return handler; \
+} \
+\
+std::shared_ptr<Endpoint::Info> Z__EDNPOINT_INFO_GET_INSTANCE_##NAME() { \
+  std::shared_ptr<Endpoint::Info> info = getEndpointInfo(#NAME); \
+  if(!info){ \
+    info = Endpoint::Info::createShared(); \
+    setEndpointInfo(#NAME, info); \
+  } \
+  return info; \
+}
+
+/*
+ *  1 - Endpoint info instance
+ *  2 - Endpoint instance
+ */
+#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL(NAME, METHOD, PATH)  \
+\
+EndpointInfoBuilder Z__CREATE_ENDPOINT_INFO_##NAME = [this](){ \
+  auto info = Z__EDNPOINT_INFO_GET_INSTANCE_##NAME(); \
+  info->name = #NAME; \
+  info->path = PATH; \
+  info->method = METHOD; \
+  return info; \
+}; \
+\
+const std::shared_ptr<Endpoint> Z__ENDPOINT_##NAME = createEndpoint(m_endpoints, \
+                                                                    Z__ENDPOINT_HANDLER_GET_INSTANCE_##NAME(this), \
+                                                                    Z__CREATE_ENDPOINT_INFO_##NAME);
+
+/**
+ * Codegen macoro to be used in `oatpp::web::server::api::ApiController` to generate Asynchronous Endpoint.
+ * @param METHOD - Http method ("GET", "POST", "PUT", etc.).
+ * @param PATH - Path to endpoint (without host).
+ * @param NAME - Name of the generated method.
+ * @return - &id:oatpp::async::Action;.
+ */
+#define ENDPOINT_ASYNC(METHOD, PATH, NAME) \
+OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL_DEFAULTS(NAME, METHOD, PATH) \
+OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL(NAME, METHOD, PATH) \
+\
+oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>&> \
+Z__PROXY_METHOD_##NAME(const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& __request) \
+{ \
+  return NAME::startForResult(this, __request); \
+} \
+\
+class NAME : public HandlerCoroutine<NAME, __ControllerType>
+
+/**
+ * Auxiliary codegen macro for `ENDPOINT_ASYNC` to generate correct constructor for Asynchronous Endpoint Coroutine.
+ * @NAME - Name of the endpoint. Exact the same name as was passed to `ENDPOINT_ASYNC` macro.
+ */
+#define ENDPOINT_ASYNC_INIT(NAME) \
+public: \
+\
+  NAME(__ControllerType* pController, \
+       const std::shared_ptr<IncomingRequest>& pRequest) \
+    : HandlerCoroutine(pController, pRequest) \
+  {}
+
+
+/**
+ * Endpoint interceptor
+ */
+#define ENDPOINT_INTERCEPTOR_ASYNC(ENDPOINT_NAME, NAME) \
+\
+Handler<oatpp::web::server::api::ApiController>::MethodAsync \
+  Z__INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME = Z__INTERCEPTOR_METHOD_SET_##ENDPOINT_NAME ##_ ##NAME(this); \
+\
+template<class T> \
+Handler<oatpp::web::server::api::ApiController>::MethodAsync Z__INTERCEPTOR_METHOD_SET_##ENDPOINT_NAME ##_ ##NAME (T* controller) { \
+  return static_cast<Handler<oatpp::web::server::api::ApiController>::MethodAsync>( \
+    Z__ENDPOINT_HANDLER_GET_INSTANCE_##ENDPOINT_NAME(controller)->setMethodAsync(&T::Z__PROXY_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME) \
+  ); \
+} \
+\
+oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>&> \
+Z__PROXY_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME(const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& request) { \
+  return Z__USER_PROXY_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME(this, request); \
+} \
+\
+template<class T> \
+oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>&> \
+Z__USER_PROXY_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME( \
+  T* controller, \
+  const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& request \
+) { \
+  auto intercepted = static_cast<typename Handler<T>::MethodAsync>(Z__INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME); \
+  return Z__USER_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME <T> (intercepted, request); \
+} \
+\
+template<class T> \
+oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>&> \
+Z__USER_INTERCEPTOR_METHOD_##ENDPOINT_NAME ##_ ##NAME( \
+  typename Handler<T>::MethodAsync intercepted, \
+  const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& request \
+)
diff --git a/src/oatpp/codegen/api_controller/base_undef.hpp b/src/oatpp/codegen/api_controller/base_undef.hpp
new file mode 100644
index 00000000..b4d8f1b7
--- /dev/null
+++ b/src/oatpp/codegen/api_controller/base_undef.hpp
@@ -0,0 +1,144 @@
+/***************************************************************************
+ *
+ * Project         _____    __   ____   _      _
+ *                (  _  )  /__\ (_  _)_| |_  _| |_
+ *                 )(_)(  /(__)\  )( (_   _)(_   _)
+ *                (_____)(__)(__)(__)  |_|    |_|
+ *
+ *
+ * Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ***************************************************************************/
+
+#undef OATPP_MACRO_API_CONTROLLER_PARAM_MACRO
+#undef OATPP_MACRO_API_CONTROLLER_PARAM_INFO
+#undef OATPP_MACRO_API_CONTROLLER_PARAM_TYPE
+#undef OATPP_MACRO_API_CONTROLLER_PARAM_NAME
+#undef OATPP_MACRO_API_CONTROLLER_PARAM_TYPE_STR
+#undef OATPP_MACRO_API_CONTROLLER_PARAM_NAME_STR
+#undef OATPP_MACRO_API_CONTROLLER_PARAM
+
+#undef REQUEST
+#undef HEADER
+#undef PATH
+#undef QUERIES
+#undef QUERY
+#undef BODY_STRING
+#undef BODY_DTO
+
+// INIT // ------------------------------------------------------
+
+#undef REST_CONTROLLER_INIT
+
+#undef OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR
+
+// REQUEST MACRO // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_REQUEST
+#undef OATPP_MACRO_API_CONTROLLER_REQUEST_INFO
+
+// HEADER MACRO // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_HEADER_1
+#undef OATPP_MACRO_API_CONTROLLER_HEADER_2
+#undef OATPP_MACRO_API_CONTROLLER_HEADER
+
+// __INFO
+
+#undef OATPP_MACRO_API_CONTROLLER_HEADER_INFO_1
+#undef OATPP_MACRO_API_CONTROLLER_HEADER_INFO_2
+#undef OATPP_MACRO_API_CONTROLLER_HEADER_INFO
+
+// PATH MACRO // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_PATH_1
+#undef OATPP_MACRO_API_CONTROLLER_PATH_2
+#undef OATPP_MACRO_API_CONTROLLER_PATH
+
+// __INFO
+
+#undef OATPP_MACRO_API_CONTROLLER_PATH_INFO_1
+#undef OATPP_MACRO_API_CONTROLLER_PATH_INFO_2
+#undef OATPP_MACRO_API_CONTROLLER_PATH_INFO
+
+// QUERIES MACRO // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_QUERIES
+#undef OATPP_MACRO_API_CONTROLLER_QUERIES_INFO
+
+// QUERY MACRO // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_QUERY_1
+#undef OATPP_MACRO_API_CONTROLLER_QUERY_2
+#undef OATPP_MACRO_API_CONTROLLER_QUERY
+
+// __INFO
+
+#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO_1
+#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO_2
+#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO
+
+// BODY_STRING MACRO // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_BODY_STRING
+
+// __INFO
+
+#undef OATPP_MACRO_API_CONTROLLER_BODY_STRING_INFO
+
+// BODY_DTO MACRO // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_BODY_DTO
+
+// __INFO
+
+#undef OATPP_MACRO_API_CONTROLLER_BODY_DTO_INFO
+
+// FOR EACH // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL
+#undef OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_PUT
+#undef OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL
+#undef OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_INFO
+
+// ENDPOINT_INFO MACRO // ------------------------------------------------------
+
+#undef ENDPOINT_INFO
+
+// ENDPOINT MACRO // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_DEFAULTS
+
+#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_0
+#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_0
+
+#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_1
+#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_1
+
+#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_0
+#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_1
+
+#undef ENDPOINT
+
+#undef ENDPOINT_INTERCEPTOR
+
+// ENDPOINT ASYNC MACRO // ------------------------------------------------------
+
+#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL_DEFAULTS
+#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL
+#undef ENDPOINT_ASYNC
+#undef ENDPOINT_ASYNC_INIT
+
+#undef ENDPOINT_INTERCEPTOR_ASYNC
diff --git a/src/oatpp/codegen/codegen_define_ApiController_.hpp b/src/oatpp/codegen/codegen_define_ApiController_.hpp
index 22aeee29..fbb58953 100644
--- a/src/oatpp/codegen/codegen_define_ApiController_.hpp
+++ b/src/oatpp/codegen/codegen_define_ApiController_.hpp
@@ -45,488 +45,8 @@
 #include "oatpp/core/macro/basic.hpp"
 #include "oatpp/core/macro/codegen.hpp"
 
-#define OATPP_MACRO_API_CONTROLLER_PARAM_MACRO(MACRO, INFO, TYPE, PARAM_LIST) MACRO(TYPE, PARAM_LIST)
-#define OATPP_MACRO_API_CONTROLLER_PARAM_INFO(MACRO, INFO, TYPE, PARAM_LIST) INFO(TYPE, PARAM_LIST)
-#define OATPP_MACRO_API_CONTROLLER_PARAM_TYPE(MACRO, INFO, TYPE, PARAM_LIST) TYPE
-#define OATPP_MACRO_API_CONTROLLER_PARAM_NAME(MACRO, INFO, TYPE, PARAM_LIST) OATPP_MACRO_FIRSTARG PARAM_LIST
-#define OATPP_MACRO_API_CONTROLLER_PARAM_TYPE_STR(MACRO, INFO, TYPE, PARAM_LIST) #TYPE
-#define OATPP_MACRO_API_CONTROLLER_PARAM_NAME_STR(MACRO, INFO, TYPE, PARAM_LIST) OATPP_MACRO_FIRSTARG_STR PARAM_LIST
-#define OATPP_MACRO_API_CONTROLLER_PARAM(MACRO, INFO, TYPE, PARAM_LIST) (MACRO, INFO, TYPE, PARAM_LIST)
-
-#define REQUEST(TYPE, ...) \
-OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_REQUEST, OATPP_MACRO_API_CONTROLLER_REQUEST_INFO, TYPE, (__VA_ARGS__))
-
-#define HEADER(TYPE, ...) \
-OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_HEADER, OATPP_MACRO_API_CONTROLLER_HEADER_INFO, TYPE, (__VA_ARGS__))
-
-#define PATH(TYPE, ...) \
-OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_PATH, OATPP_MACRO_API_CONTROLLER_PATH_INFO, TYPE, (__VA_ARGS__))
-
-#define QUERIES(TYPE, ...) \
-OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_QUERIES, OATPP_MACRO_API_CONTROLLER_QUERIES_INFO, TYPE, (__VA_ARGS__))
-
-#define QUERY(TYPE, ...) \
-OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_QUERY, OATPP_MACRO_API_CONTROLLER_QUERY_INFO, TYPE, (__VA_ARGS__))
-
-#define BODY_STRING(TYPE, ...) \
-OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_BODY_STRING, OATPP_MACRO_API_CONTROLLER_BODY_STRING_INFO, TYPE, (__VA_ARGS__))
-
-#define BODY_DTO(TYPE, ...) \
-OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_BODY_DTO, OATPP_MACRO_API_CONTROLLER_BODY_DTO_INFO, TYPE, (__VA_ARGS__))
-
-#define AUTHORIZATION(TYPE, ...) \
-OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_AUTHORIZATION, OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO, TYPE, (__VA_ARGS__))
-
-//////////////////////////////////////////////////////////////////////////
-
-#define OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(MACRO, TYPE, ...) \
-OATPP_MACRO_EXPAND(OATPP_MACRO_MACRO_SELECTOR(MACRO, (__VA_ARGS__)) (TYPE, __VA_ARGS__))
-
-//////////////////////////////////////////////////////////////////////////
-
-// REQUEST MACRO // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_REQUEST(TYPE, PARAM_LIST) \
-TYPE OATPP_MACRO_FIRSTARG PARAM_LIST = __request;
-
-#define OATPP_MACRO_API_CONTROLLER_REQUEST_INFO(TYPE, PARAM_LIST)
-
-
-// HEADER MACRO // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_HEADER_1(TYPE, NAME) \
-auto __param_str_val_##NAME = __request->getHeader(#NAME); \
-if(!__param_str_val_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, "Missing HEADER parameter '" #NAME "'"); \
-} \
-bool __param_validation_check_##NAME; \
-TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
-if(!__param_validation_check_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, "Invalid HEADER parameter '" #NAME "'. Expected type is '" #TYPE "'"); \
-}
-
-#define OATPP_MACRO_API_CONTROLLER_HEADER_2(TYPE, NAME, QUALIFIER) \
-auto __param_str_val_##NAME = __request->getHeader(QUALIFIER); \
-if(!__param_str_val_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, \
-  oatpp::String("Missing HEADER parameter '") + QUALIFIER + "'"); \
-} \
-bool __param_validation_check_##NAME; \
-TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
-if(!__param_validation_check_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, \
-                                    oatpp::String("Invalid HEADER parameter '") + \
-                                    QUALIFIER + \
-                                    "'. Expected type is '" #TYPE "'"); \
-}
-
-#define OATPP_MACRO_API_CONTROLLER_HEADER(TYPE, PARAM_LIST) \
-OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_HEADER_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
-
-// __INFO
-
-#define OATPP_MACRO_API_CONTROLLER_HEADER_INFO_1(TYPE, NAME) \
-info->headers.add(#NAME, TYPE::Class::getType());
-
-#define OATPP_MACRO_API_CONTROLLER_HEADER_INFO_2(TYPE, NAME, QUALIFIER) \
-info->headers.add(QUALIFIER, TYPE::Class::getType());
-
-#define OATPP_MACRO_API_CONTROLLER_HEADER_INFO(TYPE, PARAM_LIST) \
-OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_HEADER_INFO_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
-
-
-// PATH MACRO // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_PATH_1(TYPE, NAME) \
-auto __param_str_val_##NAME = __request->getPathVariable(#NAME); \
-if(!__param_str_val_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, "Missing PATH parameter '" #NAME "'"); \
-} \
-bool __param_validation_check_##NAME; \
-TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
-if(!__param_validation_check_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, "Invalid PATH parameter '" #NAME "'. Expected type is '" #TYPE "'"); \
-}
-
-#define OATPP_MACRO_API_CONTROLLER_PATH_2(TYPE, NAME, QUALIFIER) \
-auto __param_str_val_##NAME = __request->getPathVariable(QUALIFIER); \
-if(!__param_str_val_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, \
-  oatpp::String("Missing PATH parameter '") + QUALIFIER + "'"); \
-} \
-bool __param_validation_check_##NAME; \
-TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
-if(!__param_validation_check_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, \
-                                    oatpp::String("Invalid PATH parameter '") + \
-                                    QUALIFIER + \
-                                    "'. Expected type is '" #TYPE "'"); \
-}
-
-#define OATPP_MACRO_API_CONTROLLER_PATH(TYPE, PARAM_LIST) \
-OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_PATH_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
-
-// __INFO
-
-#define OATPP_MACRO_API_CONTROLLER_PATH_INFO_1(TYPE, NAME) \
-info->pathParams.add(#NAME, TYPE::Class::getType());
-
-#define OATPP_MACRO_API_CONTROLLER_PATH_INFO_2(TYPE, NAME, QUALIFIER) \
-info->pathParams.add(QUALIFIER, TYPE::Class::getType());
-
-#define OATPP_MACRO_API_CONTROLLER_PATH_INFO(TYPE, PARAM_LIST) \
-OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_PATH_INFO_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
-
-// QUERIES MACRO // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_QUERIES(TYPE, PARAM_LIST) \
-TYPE OATPP_MACRO_FIRSTARG PARAM_LIST = __request->getQueryParameters();
-
-#define OATPP_MACRO_API_CONTROLLER_QUERIES_INFO(TYPE, PARAM_LIST)
-
-// QUERY MACRO // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_QUERY_1(TYPE, NAME) \
-auto __param_str_val_##NAME = __request->getQueryParameter(#NAME); \
-if(!__param_str_val_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, "Missing QUERY parameter '" #NAME "'"); \
-} \
-bool __param_validation_check_##NAME; \
-TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
-if(!__param_validation_check_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, "Invalid QUERY parameter '" #NAME "'. Expected type is '" #TYPE "'"); \
-}
-
-#define OATPP_MACRO_API_CONTROLLER_QUERY_2(TYPE, NAME, QUALIFIER) \
-auto __param_str_val_##NAME = __request->getQueryParameter(QUALIFIER); \
-if(!__param_str_val_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, \
-  oatpp::String("Missing QUERY parameter '") + QUALIFIER + "'"); \
-} \
-bool __param_validation_check_##NAME; \
-TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \
-if(!__param_validation_check_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, \
-                                    oatpp::String("Invalid QUERY parameter '") + \
-                                    QUALIFIER + \
-                                    "'. Expected type is '" #TYPE "'"); \
-}
-
-#define OATPP_MACRO_API_CONTROLLER_QUERY(TYPE, PARAM_LIST) \
-OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_QUERY_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
-
-// __INFO
-
-#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO_1(TYPE, NAME) \
-info->queryParams.add(#NAME, TYPE::Class::getType());
-
-#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO_2(TYPE, NAME, QUALIFIER) \
-info->queryParams.add(QUALIFIER, TYPE::Class::getType());
-
-#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO(TYPE, PARAM_LIST) \
-OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_QUERY_INFO_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
-
-// BODY_STRING MACRO // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_BODY_STRING(TYPE, PARAM_LIST) \
-TYPE OATPP_MACRO_FIRSTARG PARAM_LIST = __request->readBodyToString();
-
-// __INFO
-
-#define OATPP_MACRO_API_CONTROLLER_BODY_STRING_INFO(TYPE, PARAM_LIST) \
-info->body.name = OATPP_MACRO_FIRSTARG_STR PARAM_LIST; \
-info->body.type = oatpp::data::mapping::type::__class::String::getType();
-
-// BODY_DTO MACRO // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_BODY_DTO(TYPE, PARAM_LIST) \
-TYPE OATPP_MACRO_FIRSTARG PARAM_LIST; \
-__request->readBodyToDto(OATPP_MACRO_FIRSTARG PARAM_LIST, getDefaultObjectMapper().get()); \
-if(!OATPP_MACRO_FIRSTARG PARAM_LIST) { \
-  return ApiController::handleError(Status::CODE_400, "Missing valid body parameter '" OATPP_MACRO_FIRSTARG_STR PARAM_LIST "'"); \
-}
-
-// __INFO
-
-#define OATPP_MACRO_API_CONTROLLER_BODY_DTO_INFO(TYPE, PARAM_LIST) \
-info->body.name = OATPP_MACRO_FIRSTARG_STR PARAM_LIST; \
-info->body.type = TYPE::Class::getType();
-
-// AUTHORIZATION MACRO // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_1(TYPE, NAME) \
-auto __param_str_val_##NAME = __request->getHeader(oatpp::web::protocol::http::Header::AUTHORIZATION); \
-std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = ApiController::handleDefaultAuthorization(__param_str_val_##NAME); \
-TYPE NAME = std::static_pointer_cast<TYPE::element_type>(__param_aosp_val_##NAME);
-
-#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_2(TYPE, NAME, AUTH_HANDLER) \
-auto __param_str_val_##NAME = __request->getHeader(oatpp::web::protocol::http::Header::AUTHORIZATION); \
-std::shared_ptr<oatpp::web::server::handler::AuthorizationHandler> __auth_handler_##NAME = AUTH_HANDLER; \
-std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = __auth_handler_##NAME->handleAuthorization(__param_str_val_##NAME); \
-TYPE NAME = std::static_pointer_cast<TYPE::element_type>(__param_aosp_val_##NAME);
-
-#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION(TYPE, PARAM_LIST) \
-OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
-
-// __INFO
-
-#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_1(TYPE, NAME) \
-auto __param_obj_##NAME = ApiController::getDefaultAuthorizationHandler(); \
-if(__param_obj_##NAME) { \
-  info->headers.add(oatpp::web::protocol::http::Header::AUTHORIZATION, oatpp::String::Class::getType()); \
-  info->headers[oatpp::web::protocol::http::Header::AUTHORIZATION].description = __param_obj_##NAME ->getScheme(); \
-  info->authorization = __param_obj_##NAME ->getScheme(); \
-} else { \
-  throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "No authorization handler set up in controller before controller was added to router or swagger-doc."); \
-}
-
-#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_2(TYPE, NAME, AUTH_HANDLER) \
-std::shared_ptr<oatpp::web::server::handler::AuthorizationHandler> __auth_handler_##NAME = AUTH_HANDLER; \
-if(__auth_handler_##NAME) { \
-  info->headers.add(oatpp::web::protocol::http::Header::AUTHORIZATION, oatpp::String::Class::getType()); \
-  info->headers[oatpp::web::protocol::http::Header::AUTHORIZATION].description = __auth_handler_##NAME->getScheme(); \
-  info->authorization = __auth_handler_##NAME->getScheme(); \
-} else { \
-  throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "Invalid authorization handler given (or not set up) in AUTHORIZATION(TYPE, NAME, AUTH_HANDLER) before controller was added to router or swagger-doc."); \
-}
-
-#define OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO(TYPE, PARAM_LIST) \
-OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_, TYPE, OATPP_MACRO_UNFOLD_VA_ARGS PARAM_LIST)
-
-// FOR EACH // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL_FIRST(INDEX, COUNT, X) \
-OATPP_MACRO_API_CONTROLLER_PARAM_TYPE X OATPP_MACRO_API_CONTROLLER_PARAM_NAME X
-
-#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL_REST(INDEX, COUNT, X) \
-, OATPP_MACRO_API_CONTROLLER_PARAM_TYPE X OATPP_MACRO_API_CONTROLLER_PARAM_NAME X
-
-#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_PUT(INDEX, COUNT, X) \
-OATPP_MACRO_API_CONTROLLER_PARAM_MACRO X
-
-#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL_FIRST(INDEX, COUNT, X) \
-OATPP_MACRO_API_CONTROLLER_PARAM_NAME X
-
-#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL_REST(INDEX, COUNT, X) \
-, OATPP_MACRO_API_CONTROLLER_PARAM_NAME X
-
-#define OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_INFO(INDEX, COUNT, X) \
-OATPP_MACRO_API_CONTROLLER_PARAM_INFO X
-
-// ENDPOINT_INFO MACRO // ------------------------------------------------------
-
-#define ENDPOINT_INFO(NAME) \
-\
-std::shared_ptr<Endpoint::Info> Z__ENDPOINT_CREATE_ADDITIONAL_INFO_##NAME() { \
-  auto info = Z__EDNPOINT_INFO_GET_INSTANCE_##NAME(); \
-  Z__ENDPOINT_ADD_INFO_##NAME(info); \
-  return info; \
-} \
-\
-const std::shared_ptr<Endpoint::Info> Z__ENDPOINT_ADDITIONAL_INFO_##NAME = Z__ENDPOINT_CREATE_ADDITIONAL_INFO_##NAME(); \
-\
-void Z__ENDPOINT_ADD_INFO_##NAME(const std::shared_ptr<Endpoint::Info>& info)
-
-// ENDPOINT MACRO // ------------------------------------------------------
-
-#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_DEFAULTS(NAME, METHOD, PATH) \
-\
-template<class T> \
-static typename Handler<T>::Method Z__ENDPOINT_METHOD_##NAME(T* controller) { \
-  (void)controller; \
-  return &T::Z__PROXY_METHOD_##NAME; \
-} \
-\
-std::shared_ptr<Endpoint::Info> Z__EDNPOINT_INFO_GET_INSTANCE_##NAME() { \
-  std::shared_ptr<Endpoint::Info> info = getEndpointInfo(#NAME); \
-  if(!info){ \
-    info = Endpoint::Info::createShared(); \
-    setEndpointInfo(#NAME, info); \
-  } \
-  return info; \
-}
-
-#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_0(NAME, METHOD, PATH)  \
-\
-EndpointInfoBuilder Z__CREATE_ENDPOINT_INFO_##NAME = [this](){ \
-  auto info = Z__EDNPOINT_INFO_GET_INSTANCE_##NAME(); \
-  info->name = #NAME; \
-  info->path = PATH; \
-  info->method = METHOD; \
-  return info; \
-}; \
-\
-const std::shared_ptr<Endpoint> Z__ENDPOINT_##NAME = createEndpoint(m_endpoints, \
-                                                        this, \
-                                                        Z__ENDPOINT_METHOD_##NAME(this), \
-                                                        nullptr, \
-                                                        Z__CREATE_ENDPOINT_INFO_##NAME);
-
-#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_0(NAME, METHOD, PATH) \
-OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_DEFAULTS(NAME, METHOD, PATH) \
-OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_0(NAME, METHOD, PATH) \
-\
-std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> \
-Z__PROXY_METHOD_##NAME(const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& __request) \
-{ \
-  (void)__request; \
-  auto resp = NAME(); \
-  auto interceptors = m_interceptors->find(#NAME); \
-  if(interceptors != nullptr) { \
-    auto interceptor = interceptors->getValue()->getFirstNode(); \
-    while(interceptor != nullptr) { \
-      resp = interceptor->getData()->operator()(resp); \
-      interceptor = interceptor->getNext(); \
-    } \
-  } \
-  return resp; \
-} \
-\
-std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> NAME()
-
-////////////////////
-
-#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_1(NAME, METHOD, PATH, ...)  \
-\
-EndpointInfoBuilder Z__CREATE_ENDPOINT_INFO_##NAME = [this](){ \
-auto info = Z__EDNPOINT_INFO_GET_INSTANCE_##NAME(); \
-  info->name = #NAME; \
-  info->path = PATH; \
-  info->method = METHOD; \
-  OATPP_MACRO_FOREACH(OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_INFO, __VA_ARGS__) \
-  return info; \
-}; \
-\
-const std::shared_ptr<Endpoint> Z__ENDPOINT_##NAME = createEndpoint(m_endpoints, \
-                                                        this, \
-                                                        Z__ENDPOINT_METHOD_##NAME(this), \
-                                                        nullptr, \
-                                                        Z__CREATE_ENDPOINT_INFO_##NAME);
-
-#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_1(NAME, METHOD, PATH, ...) \
-OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_DEFAULTS(NAME, METHOD, PATH) \
-OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_1(NAME, METHOD, PATH, __VA_ARGS__) \
-\
-std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> \
-Z__PROXY_METHOD_##NAME(const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& __request) \
-{ \
-  OATPP_MACRO_FOREACH(OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_PUT, __VA_ARGS__) \
-  auto resp = NAME( \
-    OATPP_MACRO_FOREACH_FIRST_AND_REST( \
-      OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL_FIRST, \
-      OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL_REST, \
-      __VA_ARGS__ \
-    ) \
-  ); \
-  auto interceptors = m_interceptors->find(#NAME); \
-  if(interceptors != nullptr) { \
-    auto interceptor = interceptors->getValue()->getFirstNode(); \
-    while(interceptor != nullptr) { \
-      resp = interceptor->getData()->operator()(resp); \
-      interceptor = interceptor->getNext(); \
-    } \
-  } \
-  return resp; \
-} \
-\
-std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> NAME(\
-  OATPP_MACRO_FOREACH_FIRST_AND_REST( \
-    OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL_FIRST, \
-    OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL_REST, \
-    __VA_ARGS__ \
-  ) \
-)
-
-// Chooser
-
-#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_0(METHOD, PATH, NAME) \
-OATPP_MACRO_EXPAND(OATPP_MACRO_API_CONTROLLER_ENDPOINT_0(NAME, METHOD, PATH))
-
-#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_1(METHOD, PATH, NAME, ...) \
-OATPP_MACRO_EXPAND(OATPP_MACRO_API_CONTROLLER_ENDPOINT_1(NAME, METHOD, PATH, __VA_ARGS__))
-
-/**
- * Codegen macoro to be used in `oatpp::web::server::api::ApiController` to generate Endpoint.
- * @param METHOD - Http method ("GET", "POST", "PUT", etc.).
- * @param PATH - Path to endpoint (without host).
- * @param NAME - Name of the generated method.
- * @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;.
- */
-#define ENDPOINT(METHOD, PATH, ...) \
-OATPP_MACRO_EXPAND(OATPP_MACRO_MACRO_BINARY_SELECTOR(OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_, (__VA_ARGS__)) (METHOD, PATH, __VA_ARGS__))
-
-// ENDPOINT ASYNC MACRO // ------------------------------------------------------
-
-/*
- *  1 - Method to obtain endpoint call function ptr
- *  2 - Endpoint info singleton
- */
-#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL_DEFAULTS(NAME, METHOD, PATH) \
-template<class T> \
-static typename Handler<T>::MethodAsync Z__ENDPOINT_METHOD_##NAME(T* controller) { \
-  (void)controller; \
-  return &T::Z__PROXY_METHOD_##NAME; \
-} \
-\
-std::shared_ptr<Endpoint::Info> Z__EDNPOINT_INFO_GET_INSTANCE_##NAME() { \
-  std::shared_ptr<Endpoint::Info> info = getEndpointInfo(#NAME); \
-  if(!info){ \
-    info = Endpoint::Info::createShared(); \
-    setEndpointInfo(#NAME, info); \
-  } \
-  return info; \
-}
-
-/*
- *  1 - Endpoint info instance
- *  2 - Endpoint instance
- */
-#define OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL(NAME, METHOD, PATH)  \
-\
-EndpointInfoBuilder Z__CREATE_ENDPOINT_INFO_##NAME = [this](){ \
-  auto info = Z__EDNPOINT_INFO_GET_INSTANCE_##NAME(); \
-  info->name = #NAME; \
-  info->path = PATH; \
-  info->method = METHOD; \
-  return info; \
-}; \
-\
-const std::shared_ptr<Endpoint> Z__ENDPOINT_##NAME = createEndpoint(m_endpoints, \
-                                                                    this, \
-                                                                    nullptr, \
-                                                                    Z__ENDPOINT_METHOD_##NAME(this), \
-                                                                    Z__CREATE_ENDPOINT_INFO_##NAME);
-
-/**
- * Codegen macoro to be used in `oatpp::web::server::api::ApiController` to generate Asynchronous Endpoint.
- * @param METHOD - Http method ("GET", "POST", "PUT", etc.).
- * @param PATH - Path to endpoint (without host).
- * @param NAME - Name of the generated method.
- * @return - &id:oatpp::async::Action;.
- */
-#define ENDPOINT_ASYNC(METHOD, PATH, NAME) \
-OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL_DEFAULTS(NAME, METHOD, PATH) \
-OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL(NAME, METHOD, PATH) \
-\
-oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>&> \
-Z__PROXY_METHOD_##NAME(const std::shared_ptr<oatpp::web::protocol::http::incoming::Request>& __request) \
-{ \
-  return NAME::startForResult(this, __request); \
-} \
-\
-class NAME : public HandlerCoroutine<NAME, __ControllerType>
-
-/**
- * Auxiliary codegen macro for `ENDPOINT_ASYNC` to generate correct constructor for Asynchronous Endpoint Coroutine.
- * @NAME - Name of the endpoint. Exact the same name as was passed to `ENDPOINT_ASYNC` macro.
- */
-#define ENDPOINT_ASYNC_INIT(NAME) \
-public: \
-\
-  NAME(__ControllerType* pController, \
-       const std::shared_ptr<IncomingRequest>& pRequest) \
-    : HandlerCoroutine(pController, pRequest) \
-  {}
+#include "./api_controller/base_define.hpp"
+#include "./api_controller/auth_define.hpp"
 
 // CORS MACRO // ------------------------------------------------------
 
diff --git a/src/oatpp/codegen/codegen_undef_ApiController_.hpp b/src/oatpp/codegen/codegen_undef_ApiController_.hpp
index 54a0cc20..30dd7de6 100644
--- a/src/oatpp/codegen/codegen_undef_ApiController_.hpp
+++ b/src/oatpp/codegen/codegen_undef_ApiController_.hpp
@@ -42,133 +42,8 @@
  * </ul>
  */
 
-#undef OATPP_MACRO_API_CONTROLLER_PARAM_MACRO
-#undef OATPP_MACRO_API_CONTROLLER_PARAM_INFO
-#undef OATPP_MACRO_API_CONTROLLER_PARAM_TYPE
-#undef OATPP_MACRO_API_CONTROLLER_PARAM_NAME
-#undef OATPP_MACRO_API_CONTROLLER_PARAM_TYPE_STR
-#undef OATPP_MACRO_API_CONTROLLER_PARAM_NAME_STR
-#undef OATPP_MACRO_API_CONTROLLER_PARAM
-
-#undef REQUEST
-#undef HEADER
-#undef PATH
-#undef QUERIES
-#undef QUERY
-#undef BODY_STRING
-#undef BODY_DTO
-#undef AUTHORIZATION
-
-// INIT // ------------------------------------------------------
-
-#undef REST_CONTROLLER_INIT
-
-#undef OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR
-
-// REQUEST MACRO // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_REQUEST
-#undef OATPP_MACRO_API_CONTROLLER_REQUEST_INFO
-
-// HEADER MACRO // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_HEADER_1
-#undef OATPP_MACRO_API_CONTROLLER_HEADER_2
-#undef OATPP_MACRO_API_CONTROLLER_HEADER
-
-// __INFO
-
-#undef OATPP_MACRO_API_CONTROLLER_HEADER_INFO_1
-#undef OATPP_MACRO_API_CONTROLLER_HEADER_INFO_2
-#undef OATPP_MACRO_API_CONTROLLER_HEADER_INFO
-
-// PATH MACRO // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_PATH_1
-#undef OATPP_MACRO_API_CONTROLLER_PATH_2
-#undef OATPP_MACRO_API_CONTROLLER_PATH
-
-// __INFO
-
-#undef OATPP_MACRO_API_CONTROLLER_PATH_INFO_1
-#undef OATPP_MACRO_API_CONTROLLER_PATH_INFO_2
-#undef OATPP_MACRO_API_CONTROLLER_PATH_INFO
-
-// QUERIES MACRO // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_QUERIES
-#undef OATPP_MACRO_API_CONTROLLER_QUERIES_INFO
-
-// QUERY MACRO // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_QUERY_1
-#undef OATPP_MACRO_API_CONTROLLER_QUERY_2
-#undef OATPP_MACRO_API_CONTROLLER_QUERY
-
-// __INFO
-
-#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO_1
-#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO_2
-#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO
-
-// BODY_STRING MACRO // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_BODY_STRING
-
-// __INFO
-
-#undef OATPP_MACRO_API_CONTROLLER_BODY_STRING_INFO
-
-// BODY_DTO MACRO // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_BODY_DTO
-
-// __INFO
-
-#undef OATPP_MACRO_API_CONTROLLER_BODY_DTO_INFO
-
-// AUTHORIZATION MACRO // ------------------------------------------------------
-#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_1
-#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_2
-#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION
-
-// __INFO
-#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_1
-#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO_2
-#undef OATPP_MACRO_API_CONTROLLER_AUTHORIZATION_INFO
-
-// FOR EACH // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_DECL
-#undef OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_PUT
-#undef OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_CALL
-#undef OATPP_MACRO_API_CONTROLLER_FOR_EACH_PARAM_INFO
-
-// ENDPOINT_INFO MACRO // ------------------------------------------------------
-
-#undef ENDPOINT_INFO
-
-// ENDPOINT MACRO // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_DEFAULTS
-
-#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_0
-#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_0
-
-#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_DECL_1
-#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_1
-
-#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_0
-#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_1
-
-#undef ENDPOINT
-
-// ENDPOINT ASYNC MACRO // ------------------------------------------------------
-
-#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL_DEFAULTS
-#undef OATPP_MACRO_API_CONTROLLER_ENDPOINT_ASYNC_DECL
-#undef ENDPOINT_ASYNC
-#undef ENDPOINT_ASYNC_INIT
+#include "./api_controller/base_undef.hpp"
+#include "./api_controller/auth_undef.hpp"
 
 
 // CORS MACRO // ------------------------------------------------------
diff --git a/src/oatpp/network/client/SimpleTCPConnectionProvider.cpp b/src/oatpp/network/client/SimpleTCPConnectionProvider.cpp
index 8d89c356..21edba29 100644
--- a/src/oatpp/network/client/SimpleTCPConnectionProvider.cpp
+++ b/src/oatpp/network/client/SimpleTCPConnectionProvider.cpp
@@ -126,12 +126,14 @@ oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::data::strea
   private:
     struct addrinfo* m_result;
     struct addrinfo* m_currentResult;
+    bool m_isHandleOpened;
   public:
 
     ConnectCoroutine(const oatpp::String& host, v_int32 port)
       : m_host(host)
       , m_port(port)
       , m_result(nullptr)
+      , m_isHandleOpened(false)
     {}
 
     ~ConnectCoroutine() {
@@ -141,7 +143,6 @@ oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::data::strea
     }
 
     Action act() override {
-
       auto portStr = oatpp::utils::conversion::int32ToStr(m_port);
 
       struct addrinfo hints;
@@ -170,24 +171,41 @@ oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::data::strea
 
     }
 
-
     Action iterateAddrInfoResults() {
 
+      /*
+       * Close previously opened socket here.
+       * Don't ever close socket in the method which returns action ioWait or ioRepeat
+       */
+      if(m_isHandleOpened) {
+        m_isHandleOpened = false;
+#if defined(WIN32) || defined(_WIN32)
+        ::closesocket(m_clientHandle);
+#else
+        ::close(m_clientHandle);
+#endif
+
+      }
+
       if(m_currentResult != nullptr) {
 
         m_clientHandle = socket(m_currentResult->ai_family, m_currentResult->ai_socktype, m_currentResult->ai_protocol);
 
-        if (m_clientHandle < 0) { // TODO - For windows it should check for SOCKET_ERROR constant
+#if defined(WIN32) || defined(_WIN32)
+        if (m_clientHandle == INVALID_SOCKET) {
           m_currentResult = m_currentResult->ai_next;
           return repeat();
         }
-
-#if defined(WIN32) || defined(_WIN32)
         u_long flags = 1;
         ioctlsocket(m_clientHandle, FIONBIO, &flags);
 #else
+        if (m_clientHandle < 0) {
+          m_currentResult = m_currentResult->ai_next;
+          return repeat();
+        }
         fcntl(m_clientHandle, F_SETFL, O_NONBLOCK);
 #endif
+
 #ifdef SO_NOSIGPIPE
         int yes = 1;
         v_int32 ret = setsockopt(m_clientHandle, SOL_SOCKET, SO_NOSIGPIPE, &yes, sizeof(int));
@@ -195,7 +213,10 @@ oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::data::strea
           OATPP_LOGD("[oatpp::network::client::SimpleTCPConnectionProvider::getConnectionAsync()]", "Warning. Failed to set %s for socket", "SO_NOSIGPIPE");
         }
 #endif
+
+        m_isHandleOpened = true;
         return yieldTo(&ConnectCoroutine::doConnect);
+
       }
 
       return error<Error>("[oatpp::network::client::SimpleTCPConnectionProvider::getConnectionAsync()]: Error. Can't connect.");
@@ -203,7 +224,6 @@ oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::data::strea
     }
 
     Action doConnect() {
-
       errno = 0;
 
       auto res = connect(m_clientHandle, m_currentResult->ai_addr, m_currentResult->ai_addrlen);
@@ -221,8 +241,6 @@ oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::data::strea
         return ioRepeat(m_clientHandle, oatpp::async::Action::IOEventType::IO_EVENT_WRITE);
       }
 
-	    ::closesocket(m_clientHandle);
-
 #else
 
       if(res == 0 || errno == EISCONN) {
@@ -234,8 +252,6 @@ oatpp::async::CoroutineStarterForResult<const std::shared_ptr<oatpp::data::strea
         return ioRepeat(m_clientHandle, oatpp::async::Action::IOEventType::IO_EVENT_WRITE);
       }
 
-	    ::close(m_clientHandle);
-
 #endif
 
       m_currentResult = m_currentResult->ai_next;
diff --git a/src/oatpp/web/protocol/http/incoming/Request.cpp b/src/oatpp/web/protocol/http/incoming/Request.cpp
index 21b8fb9d..be94e273 100644
--- a/src/oatpp/web/protocol/http/incoming/Request.cpp
+++ b/src/oatpp/web/protocol/http/incoming/Request.cpp
@@ -89,6 +89,19 @@ std::shared_ptr<const http::incoming::BodyDecoder> Request::getBodyDecoder() con
   return m_bodyDecoder;
 }
 
+void Request::putHeader(const oatpp::data::share::StringKeyLabelCI_FAST& key, const oatpp::data::share::StringKeyLabel& value) {
+  m_headers[key] = value;
+}
+
+bool Request::putHeaderIfNotExists(const oatpp::data::share::StringKeyLabelCI_FAST& key, const oatpp::data::share::StringKeyLabel& value) {
+  auto it = m_headers.find(key);
+  if(it == m_headers.end()) {
+    m_headers.insert({key, value});
+    return true;
+  }
+  return false;
+}
+
 oatpp::String Request::getHeader(const oatpp::data::share::StringKeyLabelCI_FAST& headerName) const{
   auto it = m_headers.find(headerName);
   if(it != m_headers.end()) {
diff --git a/src/oatpp/web/protocol/http/incoming/Request.hpp b/src/oatpp/web/protocol/http/incoming/Request.hpp
index 0091fc14..3d0c3bae 100644
--- a/src/oatpp/web/protocol/http/incoming/Request.hpp
+++ b/src/oatpp/web/protocol/http/incoming/Request.hpp
@@ -126,10 +126,25 @@ public:
    */
   std::shared_ptr<const http::incoming::BodyDecoder> getBodyDecoder() const;
 
+  /**
+   * Add http header.
+   * @param key - &id:oatpp::data::share::StringKeyLabelCI_FAST;.
+   * @param value - &id:oatpp::data::share::StringKeyLabel;.
+   */
+  void putHeader(const oatpp::data::share::StringKeyLabelCI_FAST& key, const oatpp::data::share::StringKeyLabel& value);
+
+  /**
+   * Add http header if not already exists.
+   * @param key - &id:oatpp::data::share::StringKeyLabelCI_FAST;.
+   * @param value - &id:oatpp::data::share::StringKeyLabel;.
+   * @return - `true` if header was added.
+   */
+  bool putHeaderIfNotExists(const oatpp::data::share::StringKeyLabelCI_FAST& key, const oatpp::data::share::StringKeyLabel& value);
+
   /**
    * Get header value
-   * @param headerName
-   * @return header value
+   * @param headerName - &id:oatpp::data::share::StringKeyLabelCI_FAST;.
+   * @return - &id:oatpp::String;.
    */
   oatpp::String getHeader(const oatpp::data::share::StringKeyLabelCI_FAST& headerName) const;
 
diff --git a/src/oatpp/web/protocol/http/outgoing/Response.cpp b/src/oatpp/web/protocol/http/outgoing/Response.cpp
index 9b21e24e..a9ebcbad 100644
--- a/src/oatpp/web/protocol/http/outgoing/Response.cpp
+++ b/src/oatpp/web/protocol/http/outgoing/Response.cpp
@@ -60,6 +60,14 @@ bool Response::putHeaderIfNotExists(const oatpp::data::share::StringKeyLabelCI_F
   return false;
 }
 
+oatpp::String Response::getHeader(const oatpp::data::share::StringKeyLabelCI_FAST& headerName) const {
+  auto it = m_headers.find(headerName);
+  if(it != m_headers.end()) {
+    return it->second.toString();
+  }
+  return nullptr;
+}
+
 void Response::setConnectionUpgradeHandler(const std::shared_ptr<oatpp::network::server::ConnectionHandler>& handler) {
   m_connectionUpgradeHandler = handler;
 }
diff --git a/src/oatpp/web/protocol/http/outgoing/Response.hpp b/src/oatpp/web/protocol/http/outgoing/Response.hpp
index 981694e9..e4099fc3 100644
--- a/src/oatpp/web/protocol/http/outgoing/Response.hpp
+++ b/src/oatpp/web/protocol/http/outgoing/Response.hpp
@@ -100,6 +100,13 @@ public:
    */
   bool putHeaderIfNotExists(const oatpp::data::share::StringKeyLabelCI_FAST& key, const oatpp::data::share::StringKeyLabel& value);
 
+  /**
+   * Get header value
+   * @param headerName - &id:oatpp::data::share::StringKeyLabelCI_FAST;.
+   * @return - &id:oatpp::String;.
+   */
+  oatpp::String getHeader(const oatpp::data::share::StringKeyLabelCI_FAST& headerName) const;
+
   /**
    * Set connection upgreade header. <br>
    * Use it together with corresponding headers being set when Response is created as: <br>
diff --git a/src/oatpp/web/server/api/ApiController.cpp b/src/oatpp/web/server/api/ApiController.cpp
index 4256805d..d03c94a7 100644
--- a/src/oatpp/web/server/api/ApiController.cpp
+++ b/src/oatpp/web/server/api/ApiController.cpp
@@ -48,6 +48,14 @@ std::shared_ptr<ApiController::Endpoint::Info> ApiController::getEndpointInfo(co
   return m_endpointInfo[endpointName];
 }
 
+void ApiController::setEndpointHandler(const std::string& endpointName, const std::shared_ptr<RequestHandler>& handler) {
+  m_endpointHandlers[endpointName] = handler;
+}
+
+std::shared_ptr<ApiController::RequestHandler> ApiController::getEndpointHandler(const std::string& endpointName) {
+  return m_endpointHandlers[endpointName];
+}
+
 void ApiController::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){
   m_errorHandler = errorHandler;
 }
diff --git a/src/oatpp/web/server/api/ApiController.hpp b/src/oatpp/web/server/api/ApiController.hpp
index 35288318..91469b30 100644
--- a/src/oatpp/web/server/api/ApiController.hpp
+++ b/src/oatpp/web/server/api/ApiController.hpp
@@ -105,6 +105,11 @@ public:
    */
   typedef oatpp::collection::LinkedList<std::shared_ptr<Endpoint>> Endpoints;
 
+  /**
+   * Convenience typedef for &id:oatpp::web::server::HttpRequestHandler;.
+   */
+  typedef oatpp::web::server::HttpRequestHandler RequestHandler;
+
   /**
    * Convenience typedef for &id:oatpp::web::server::handler::AuthorizationHandler;.
    */
@@ -185,7 +190,7 @@ protected:
    * Handler which subscribes to specific URL in Router and delegates calls endpoints 
    */
   template<class T>
-  class Handler : public oatpp::web::server::HttpRequestHandler {
+  class Handler : public RequestHandler {
   public:
     typedef std::shared_ptr<OutgoingResponse> (T::*Method)(const std::shared_ptr<protocol::http::incoming::Request>&);
     typedef oatpp::async::CoroutineStarterForResult<const std::shared_ptr<OutgoingResponse>&>
@@ -221,8 +226,55 @@ protected:
       }
       throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "Using Async model for non async endpoint");
     }
+
+    Method setMethod(Method method) {
+      auto prev = m_method;
+      m_method = method;
+      return prev;
+    }
+
+    Method getMethod() {
+      return m_method;
+    }
+
+    MethodAsync setMethodAsync(MethodAsync methodAsync) {
+      auto prev = m_methodAsync;
+      m_methodAsync = methodAsync;
+      return prev;
+    }
+
+    MethodAsync getMethodAsync() {
+      return m_methodAsync;
+    }
     
   };
+
+protected:
+
+  /*
+   * Set endpoint info by endpoint name. (Endpoint name is the 'NAME' parameter of the ENDPOINT macro)
+   * Info should be set before call to addEndpointsToRouter();
+   */
+  void setEndpointInfo(const std::string& endpointName, const std::shared_ptr<Endpoint::Info>& info);
+
+  /*
+   * Get endpoint info by endpoint name. (Endpoint name is the 'NAME' parameter of the ENDPOINT macro)
+   */
+  std::shared_ptr<Endpoint::Info> getEndpointInfo(const std::string& endpointName);
+
+  /*
+   * Set endpoint Request handler.
+   * @param endpointName
+   * @param handler
+   */
+  void setEndpointHandler(const std::string& endpointName, const std::shared_ptr<RequestHandler>& handler);
+
+  /*
+   * Get endpoint Request handler.
+   * @param endpointName
+   * @return
+   */
+  std::shared_ptr<RequestHandler> getEndpointHandler(const std::string& endpointName);
   
 protected:
   std::shared_ptr<Endpoints> m_endpoints;
@@ -231,6 +283,7 @@ protected:
   std::shared_ptr<handler::AuthorizationHandler> m_defaultAuthorizationHandler;
   std::shared_ptr<oatpp::data::mapping::ObjectMapper> m_defaultObjectMapper;
   std::unordered_map<std::string, std::shared_ptr<Endpoint::Info>> m_endpointInfo;
+  std::unordered_map<std::string, std::shared_ptr<RequestHandler>> m_endpointHandlers;
 public:
   ApiController(const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& defaultObjectMapper)
     : m_endpoints(Endpoints::createShared())
@@ -242,11 +295,9 @@ public:
   
   template<class T>
   static std::shared_ptr<Endpoint> createEndpoint(const std::shared_ptr<Endpoints>& endpoints,
-                                                  T* controller,
-                                                  typename Handler<T>::Method method,
-                                                  typename Handler<T>::MethodAsync methodAsync,
-                                                  const EndpointInfoBuilder& infoBuilder){
-    auto handler = Handler<T>::createShared(controller, method, methodAsync);
+                                                  const std::shared_ptr<Handler<T>>& handler,
+                                                  const EndpointInfoBuilder& infoBuilder)
+  {
     auto endpoint = Endpoint::createShared(handler, infoBuilder);
     endpoints->pushBack(endpoint);
     return endpoint;
@@ -272,17 +323,6 @@ public:
    * Get list of Endpoints created via ENDPOINT macro
    */
   std::shared_ptr<Endpoints> getEndpoints();
-  
-  /**
-   * Set endpoint info by endpoint name. (Endpoint name is the 'NAME' parameter of the ENDPOINT macro)
-   * Info should be set before call to addEndpointsToRouter();
-   */
-  void setEndpointInfo(const std::string& endpointName, const std::shared_ptr<Endpoint::Info>& info);
-  
-  /**
-   * Get endpoint info by endpoint name. (Endpoint name is the 'NAME' parameter of the ENDPOINT macro)
-   */
-  std::shared_ptr<Endpoint::Info> getEndpointInfo(const std::string& endpointName);
 
   /**
    * [under discussion]
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index a1e473c5..2e7c28c7 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -51,14 +51,16 @@ add_executable(oatppAllTests
         oatpp/web/FullAsyncTest.hpp
         oatpp/web/FullTest.cpp
         oatpp/web/FullTest.hpp
+        oatpp/web/FullAsyncClientTest.cpp
+        oatpp/web/FullAsyncClientTest.hpp
         oatpp/web/app/Client.hpp
         oatpp/web/app/BearerAuthorizationController.hpp
         oatpp/web/app/BasicAuthorizationController.hpp
         oatpp/web/app/Controller.hpp
         oatpp/web/app/ControllerAsync.hpp
         oatpp/web/app/DTOs.hpp
-        oatpp/web/FullAsyncClientTest.cpp
-        oatpp/web/FullAsyncClientTest.hpp
+        oatpp/web/app/ControllerWithInterceptors.hpp
+        oatpp/web/app/ControllerWithInterceptorsAsync.hpp
 )
 
 target_link_libraries(oatppAllTests PRIVATE oatpp PRIVATE oatpp-test)
diff --git a/test/oatpp/web/FullAsyncClientTest.cpp b/test/oatpp/web/FullAsyncClientTest.cpp
index 05b8d9b7..b8e9efb0 100644
--- a/test/oatpp/web/FullAsyncClientTest.cpp
+++ b/test/oatpp/web/FullAsyncClientTest.cpp
@@ -154,6 +154,7 @@ public:
     } else {
       OATPP_LOGE("[FullAsyncClientTest::ClientCoroutine_getRootAsync::handleError()]", "Error. %s", error->what());
     }
+    OATPP_ASSERT(!"Error");
     return error;
   }
 
@@ -195,6 +196,7 @@ public:
     } else {
       OATPP_LOGE("[FullAsyncClientTest::ClientCoroutine_postBodyAsync::handleError()]", "Error. %s", error->what());
     }
+    OATPP_ASSERT(!"Error");
     return error;
   }
 
@@ -242,6 +244,7 @@ public:
         OATPP_LOGE("[FullAsyncClientTest::ClientCoroutine_echoBodyAsync::handleError()]", "Error. %s", error->what());
       }
     }
+    OATPP_ASSERT(!"Error");
     return error;
   }
 
diff --git a/test/oatpp/web/FullAsyncTest.cpp b/test/oatpp/web/FullAsyncTest.cpp
index 743daef0..d2f53bf4 100644
--- a/test/oatpp/web/FullAsyncTest.cpp
+++ b/test/oatpp/web/FullAsyncTest.cpp
@@ -26,6 +26,7 @@
 
 #include "oatpp/web/app/Client.hpp"
 
+#include "oatpp/web/app/ControllerWithInterceptorsAsync.hpp"
 #include "oatpp/web/app/ControllerAsync.hpp"
 
 #include "oatpp/web/client/HttpRequestExecutor.hpp"
@@ -143,6 +144,7 @@ void FullAsyncTest::onRun() {
   oatpp::test::web::ClientServerTestRunner runner;
 
   runner.addController(app::ControllerAsync::createShared());
+  runner.addController(app::ControllerWithInterceptorsAsync::createShared());
 
   runner.run([this, &runner] {
 
@@ -256,6 +258,13 @@ void FullAsyncTest::onRun() {
 
       }
 
+      { // test interceptor GET
+        auto response = client->getInterceptors(connection);
+        OATPP_ASSERT(response->getStatusCode() == 200);
+        auto value = response->readBodyToString();
+        OATPP_ASSERT(value == "Hello World Async!!!");
+      }
+
       if((i + 1) % iterationsStep == 0) {
         auto ticks = oatpp::base::Environment::getMicroTickCount() - lastTick;
         lastTick = oatpp::base::Environment::getMicroTickCount();
diff --git a/test/oatpp/web/FullTest.cpp b/test/oatpp/web/FullTest.cpp
index 2b2c8355..2d746b8d 100644
--- a/test/oatpp/web/FullTest.cpp
+++ b/test/oatpp/web/FullTest.cpp
@@ -26,6 +26,7 @@
 
 #include "oatpp/web/app/Client.hpp"
 
+#include "oatpp/web/app/ControllerWithInterceptors.hpp"
 #include "oatpp/web/app/Controller.hpp"
 #include "oatpp/web/app/BasicAuthorizationController.hpp"
 #include "oatpp/web/app/BearerAuthorizationController.hpp"
@@ -140,6 +141,7 @@ void FullTest::onRun() {
   oatpp::test::web::ClientServerTestRunner runner;
 
   runner.addController(app::Controller::createShared());
+  runner.addController(app::ControllerWithInterceptors::createShared());
   runner.addController(app::DefaultBasicAuthorizationController::createShared());
   runner.addController(app::BasicAuthorizationController::createShared());
   runner.addController(app::BearerAuthorizationController::createShared());
@@ -431,6 +433,13 @@ void FullTest::onRun() {
 
       }
 
+      { // test interceptors
+        auto response = client->getInterceptors(connection);
+        OATPP_ASSERT(response->getStatusCode() == 200);
+        auto value = response->readBodyToString();
+        OATPP_ASSERT(value == "Hello World!!!");
+      }
+
       if((i + 1) % iterationsStep == 0) {
         auto ticks = oatpp::base::Environment::getMicroTickCount() - lastTick;
         lastTick = oatpp::base::Environment::getMicroTickCount();
diff --git a/test/oatpp/web/app/Client.hpp b/test/oatpp/web/app/Client.hpp
index 04772f00..2f3587da 100644
--- a/test/oatpp/web/app/Client.hpp
+++ b/test/oatpp/web/app/Client.hpp
@@ -65,6 +65,8 @@ public:
   API_CALL("GET", "chunked/{text-value}/{num-iterations}", getChunked, PATH(String, text, "text-value"), PATH(Int32, numIterations, "num-iterations"))
   API_CALL("POST", "test/multipart/{chunk-size}", multipartTest, PATH(Int32, chunkSize, "chunk-size"), BODY(std::shared_ptr<MultipartBody>, body))
 
+  API_CALL("GET", "test/interceptors", getInterceptors)
+
   API_CALL_ASYNC("GET", "/", getRootAsync)
   API_CALL_ASYNC("GET", "/", getRootAsyncWithCKA, HEADER(String, connection, "Connection"))
   API_CALL_ASYNC("GET", "params/{param}", getWithParamsAsync, PATH(String, param))
diff --git a/test/oatpp/web/app/Controller.hpp b/test/oatpp/web/app/Controller.hpp
index 082d4cda..6512da36 100644
--- a/test/oatpp/web/app/Controller.hpp
+++ b/test/oatpp/web/app/Controller.hpp
@@ -61,8 +61,16 @@ public:
   
 #include OATPP_CODEGEN_BEGIN(ApiController)
 
+  /*ENDPOINT_INTERCEPTOR(root, inter1) {
+    OATPP_LOGD("aaa", "inter1");
+    return (this->*prevMethod)(request);
+  }*/
+  /*ENDPOINT_INTERCEPTOR(root, inter2) {
+    OATPP_LOGD("aaa", "inter2");
+    return (this->*prevMethod)(request);
+  }*/
   ENDPOINT("GET", "/", root) {
-//    OATPP_LOGV(TAG, "GET '/'");
+    //OATPP_LOGV(TAG, "GET '/'");
     return createResponse(Status::CODE_200, "Hello World!!!");
   }
 
diff --git a/test/oatpp/web/app/ControllerAsync.hpp b/test/oatpp/web/app/ControllerAsync.hpp
index 6e843040..9e1d3dbc 100644
--- a/test/oatpp/web/app/ControllerAsync.hpp
+++ b/test/oatpp/web/app/ControllerAsync.hpp
@@ -60,7 +60,16 @@ public:
   }
   
 #include OATPP_CODEGEN_BEGIN(ApiController)
-  
+
+//  ENDPOINT_INTERCEPTOR_ASYNC(Root, inter1) {
+//    OATPP_LOGD("aaa", "inter1");
+//    return (this->*prevMethod)(request);
+//  }
+//  ENDPOINT_INTERCEPTOR(root, inter2) {
+//    OATPP_LOGD("aaa", "inter2");
+//    return (this->*prevMethod)(request);
+//  }
+
   ENDPOINT_ASYNC("GET", "/", Root) {
     
     ENDPOINT_ASYNC_INIT(Root)
diff --git a/test/oatpp/web/app/ControllerWithInterceptors.hpp b/test/oatpp/web/app/ControllerWithInterceptors.hpp
new file mode 100644
index 00000000..4b7036c1
--- /dev/null
+++ b/test/oatpp/web/app/ControllerWithInterceptors.hpp
@@ -0,0 +1,114 @@
+/***************************************************************************
+ *
+ * Project         _____    __   ____   _      _
+ *                (  _  )  /__\ (_  _)_| |_  _| |_
+ *                 )(_)(  /(__)\  )( (_   _)(_   _)
+ *                (_____)(__)(__)(__)  |_|    |_|
+ *
+ *
+ * Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ***************************************************************************/
+
+#ifndef oatpp_test_web_app_ControllerWithInterceptors_hpp
+#define oatpp_test_web_app_ControllerWithInterceptors_hpp
+
+#include "oatpp/web/server/api/ApiController.hpp"
+#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
+#include "oatpp/core/utils/ConversionUtils.hpp"
+#include "oatpp/core/macro/codegen.hpp"
+#include "oatpp/core/macro/component.hpp"
+
+#include <sstream>
+
+namespace oatpp { namespace test { namespace web { namespace app {
+
+namespace multipart = oatpp::web::mime::multipart;
+
+class ControllerWithInterceptors : public oatpp::web::server::api::ApiController {
+private:
+  static constexpr const char* TAG = "test::web::app::ControllerWithInterceptors";
+public:
+  ControllerWithInterceptors(const std::shared_ptr<ObjectMapper>& objectMapper)
+    : oatpp::web::server::api::ApiController(objectMapper)
+  {}
+public:
+
+  static std::shared_ptr<ControllerWithInterceptors> createShared(const std::shared_ptr<ObjectMapper>& objectMapper = OATPP_GET_COMPONENT(std::shared_ptr<ObjectMapper>)){
+    return std::make_shared<ControllerWithInterceptors>(objectMapper);
+  }
+
+#include OATPP_CODEGEN_BEGIN(ApiController)
+
+  ENDPOINT_INTERCEPTOR(interceptor, inter1) {
+
+    /* assert order of interception */
+    OATPP_ASSERT(request->getHeader("header-in-inter2") == "inter2");
+    OATPP_ASSERT(request->getHeader("header-in-inter3") == "inter3");
+    /********************************/
+
+    request->putHeader("header-in-inter1", "inter1");
+    auto response = (this->*intercepted)(request);
+    response->putHeader("header-out-inter1", "inter1");
+    return response;
+
+  }
+  ENDPOINT_INTERCEPTOR(interceptor, inter2) {
+
+    /* assert order of interception */
+    OATPP_ASSERT(request->getHeader("header-in-inter3") == "inter3");
+    /********************************/
+
+    request->putHeader("header-in-inter2", "inter2");
+    auto response = (this->*intercepted)(request);
+    response->putHeader("header-out-inter2", "inter2");
+    return response;
+
+  }
+  ENDPOINT_INTERCEPTOR(interceptor, inter3) {
+    request->putHeader("header-in-inter3", "inter3");
+    auto response = (this->*intercepted)(request);
+    response->putHeader("header-out-inter3", "inter3");
+    return response;
+  }
+  ENDPOINT_INTERCEPTOR(interceptor, asserter) {
+    auto response = (this->*intercepted)(request);
+
+    OATPP_ASSERT(response->getHeader("header-out-inter1") == "inter1");
+    OATPP_ASSERT(response->getHeader("header-out-inter2") == "inter2");
+    OATPP_ASSERT(response->getHeader("header-out-inter3") == "inter3");
+
+    return response;
+  }
+  ENDPOINT("GET", "test/interceptors", interceptor,
+           REQUEST(std::shared_ptr<IncomingRequest>, request))
+  {
+
+    OATPP_ASSERT(request->getHeader("header-in-inter1") == "inter1");
+    OATPP_ASSERT(request->getHeader("header-in-inter2") == "inter2");
+    OATPP_ASSERT(request->getHeader("header-in-inter3") == "inter3");
+
+    return createResponse(Status::CODE_200, "Hello World!!!");
+
+  }
+
+
+#include OATPP_CODEGEN_END(ApiController)
+
+};
+
+}}}}
+
+#endif /* oatpp_test_web_app_ControllerWithInterceptors_hpp */
diff --git a/test/oatpp/web/app/ControllerWithInterceptorsAsync.hpp b/test/oatpp/web/app/ControllerWithInterceptorsAsync.hpp
new file mode 100644
index 00000000..f2c1825f
--- /dev/null
+++ b/test/oatpp/web/app/ControllerWithInterceptorsAsync.hpp
@@ -0,0 +1,160 @@
+/***************************************************************************
+ *
+ * Project         _____    __   ____   _      _
+ *                (  _  )  /__\ (_  _)_| |_  _| |_
+ *                 )(_)(  /(__)\  )( (_   _)(_   _)
+ *                (_____)(__)(__)(__)  |_|    |_|
+ *
+ *
+ * Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ***************************************************************************/
+
+#ifndef oatpp_test_web_app_ControllerWithInterceptorsAsync_hpp
+#define oatpp_test_web_app_ControllerWithInterceptorsAsync_hpp
+
+#include "oatpp/web/server/api/ApiController.hpp"
+#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
+#include "oatpp/core/utils/ConversionUtils.hpp"
+#include "oatpp/core/macro/codegen.hpp"
+#include "oatpp/core/macro/component.hpp"
+
+#include <sstream>
+
+namespace oatpp { namespace test { namespace web { namespace app {
+
+namespace multipart = oatpp::web::mime::multipart;
+
+class ControllerWithInterceptorsAsync : public oatpp::web::server::api::ApiController {
+private:
+  static constexpr const char* TAG = "test::web::app::ControllerWithInterceptorsAsync";
+public:
+  ControllerWithInterceptorsAsync(const std::shared_ptr<ObjectMapper>& objectMapper)
+    : oatpp::web::server::api::ApiController(objectMapper)
+  {}
+public:
+
+  static std::shared_ptr<ControllerWithInterceptorsAsync> createShared(const std::shared_ptr<ObjectMapper>& objectMapper = OATPP_GET_COMPONENT(std::shared_ptr<ObjectMapper>)){
+    return std::make_shared<ControllerWithInterceptorsAsync>(objectMapper);
+  }
+
+#include OATPP_CODEGEN_BEGIN(ApiController)
+
+  ENDPOINT_INTERCEPTOR_ASYNC(Interceptor, inter1) {
+
+    /* assert order of interception */
+    OATPP_ASSERT(request->getHeader("header-in-inter2") == "inter2");
+    OATPP_ASSERT(request->getHeader("header-in-inter3") == "inter3");
+    /********************************/
+
+    request->putHeader("header-in-inter1", "inter1");
+    return (this->*intercepted)(request);
+
+  }
+  ENDPOINT_INTERCEPTOR_ASYNC(Interceptor, inter2) {
+
+    /* assert order of interception */
+    OATPP_ASSERT(request->getHeader("header-in-inter3") == "inter3");
+    /********************************/
+
+    request->putHeader("header-in-inter2", "inter2");
+    return (this->*intercepted)(request);
+
+  }
+  ENDPOINT_INTERCEPTOR_ASYNC(Interceptor, inter3) {
+
+    class IterceptorCoroutine : public oatpp::async::CoroutineWithResult<IterceptorCoroutine, const std::shared_ptr<OutgoingResponse>&> {
+    private:
+      ControllerWithInterceptorsAsync* m_this;
+      Handler<ControllerWithInterceptorsAsync>::MethodAsync m_intercepted;
+      std::shared_ptr<IncomingRequest> m_request;
+    public:
+
+      IterceptorCoroutine(ControllerWithInterceptorsAsync* _this,
+                          const Handler<ControllerWithInterceptorsAsync>::MethodAsync& intercepted,
+                          const std::shared_ptr<IncomingRequest>& request)
+        : m_this(_this)
+        , m_intercepted(intercepted)
+        , m_request(request)
+      {}
+
+      oatpp::async::Action act() override {
+        m_request->putHeader("header-in-inter3", "inter3");
+        return (m_this->*m_intercepted)(m_request).callbackTo(&IterceptorCoroutine::onResponse);
+      }
+
+      oatpp::async::Action onResponse(const std::shared_ptr<OutgoingResponse>& response) {
+        response->putHeader("header-out-inter3", "inter3");
+        return this->_return(response);
+      }
+
+    };
+
+    return IterceptorCoroutine::startForResult(this, intercepted, request);
+
+  }
+  ENDPOINT_INTERCEPTOR_ASYNC(Interceptor, asserter) {
+
+    class IterceptorCoroutine : public oatpp::async::CoroutineWithResult<IterceptorCoroutine, const std::shared_ptr<OutgoingResponse>&> {
+    private:
+      ControllerWithInterceptorsAsync* m_this;
+      Handler<ControllerWithInterceptorsAsync>::MethodAsync m_intercepted;
+      std::shared_ptr<IncomingRequest> m_request;
+    public:
+
+      IterceptorCoroutine(ControllerWithInterceptorsAsync* _this,
+                          const Handler<ControllerWithInterceptorsAsync>::MethodAsync& intercepted,
+                          const std::shared_ptr<IncomingRequest>& request)
+        : m_this(_this)
+        , m_intercepted(intercepted)
+        , m_request(request)
+      {}
+
+      oatpp::async::Action act() override {
+        return (m_this->*m_intercepted)(m_request).callbackTo(&IterceptorCoroutine::onResponse);
+      }
+
+      oatpp::async::Action onResponse(const std::shared_ptr<OutgoingResponse>& response) {
+        OATPP_ASSERT(response->getHeader("header-out-inter3") == "inter3");
+        return this->_return(response);
+      }
+
+    };
+
+    return IterceptorCoroutine::startForResult(this, intercepted, request);
+
+  }
+  ENDPOINT_ASYNC("GET", "test/interceptors", Interceptor) {
+
+    ENDPOINT_ASYNC_INIT(Interceptor)
+
+    Action act() {
+
+      OATPP_ASSERT(request->getHeader("header-in-inter1") == "inter1");
+      OATPP_ASSERT(request->getHeader("header-in-inter2") == "inter2");
+      OATPP_ASSERT(request->getHeader("header-in-inter3") == "inter3");
+
+      return _return(controller->createResponse(Status::CODE_200, "Hello World Async!!!"));
+    }
+
+  };
+
+#include OATPP_CODEGEN_END(ApiController)
+
+};
+
+}}}}
+
+#endif /* oatpp_test_web_app_ControllerWithInterceptorsAsync_hpp */