Browse Source

Add new virtual handleError method to handle the original error

Jeremy Guinn 2 years ago
parent
commit
eff27e6aa5

+ 58 - 29
src/oatpp/codegen/api_controller/base_define.hpp

@@ -70,28 +70,35 @@ const auto& OATPP_MACRO_FIRSTARG PARAM_LIST = __request;
 
 #define OATPP_MACRO_API_CONTROLLER_HEADER_1(TYPE, NAME) \
 const auto& __param_str_val_##NAME = __request->getHeader(#NAME); \
-if(!__param_str_val_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, "Missing HEADER parameter '" #NAME "'"); \
+if(!__param_str_val_##NAME){                            \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, "Missing HEADER parameter '" #NAME "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 } \
 bool __param_validation_check_##NAME; \
 const auto& NAME = ApiController::TypeInterpretation<TYPE>::fromString(#TYPE, __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 "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, "Invalid HEADER parameter '" #NAME "'. Expected type is '" #TYPE "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 }
 
 #define OATPP_MACRO_API_CONTROLLER_HEADER_2(TYPE, NAME, QUALIFIER) \
 const 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 + "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, oatpp::String("Missing HEADER parameter '") + QUALIFIER + "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 } \
 bool __param_validation_check_##NAME; \
 const auto& NAME = ApiController::TypeInterpretation<TYPE>::fromString(#TYPE, __param_str_val_##NAME, __param_validation_check_##NAME); \
 if(!__param_validation_check_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, \
                                     oatpp::String("Invalid HEADER parameter '") + \
                                     QUALIFIER + \
                                     "'. Expected type is '" #TYPE "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 }
 
 #define OATPP_MACRO_API_CONTROLLER_HEADER(TYPE, PARAM_LIST) \
@@ -114,27 +121,35 @@ OATPP_MACRO_API_CONTROLLER_MACRO_SELECTOR(OATPP_MACRO_API_CONTROLLER_HEADER_INFO
 #define OATPP_MACRO_API_CONTROLLER_PATH_1(TYPE, NAME) \
 const auto& __param_str_val_##NAME = __request->getPathVariable(#NAME); \
 if(!__param_str_val_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, "Missing PATH parameter '" #NAME "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, "Missing PATH parameter '" #NAME "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 } \
 bool __param_validation_check_##NAME; \
 const auto& NAME = ApiController::TypeInterpretation<TYPE>::fromString(#TYPE, __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 "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, "Invalid PATH parameter '" #NAME "'. Expected type is '" #TYPE "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 }
 
 #define OATPP_MACRO_API_CONTROLLER_PATH_2(TYPE, NAME, QUALIFIER) \
 const 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 + "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, \
+                                             oatpp::String("Missing PATH parameter '") + QUALIFIER + "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 } \
 bool __param_validation_check_##NAME; \
 const auto NAME = ApiController::TypeInterpretation<TYPE>::fromString(#TYPE, __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 "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, \
+                                             oatpp::String("Invalid PATH parameter '") + \
+                                             QUALIFIER + \
+                                             "'. Expected type is '" #TYPE "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 }
 
 #define OATPP_MACRO_API_CONTROLLER_PATH(TYPE, PARAM_LIST) \
@@ -163,27 +178,35 @@ const auto& OATPP_MACRO_FIRSTARG PARAM_LIST = __request->getQueryParameters();
 #define OATPP_MACRO_API_CONTROLLER_QUERY_1(TYPE, NAME) \
 const auto& __param_str_val_##NAME = __request->getQueryParameter(#NAME); \
 if(!__param_str_val_##NAME){ \
-  return ApiController::handleError(Status::CODE_400, "Missing QUERY parameter '" #NAME "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, "Missing QUERY parameter '" #NAME "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 } \
 bool __param_validation_check_##NAME; \
 const auto& NAME = ApiController::TypeInterpretation<TYPE>::fromString(#TYPE, __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 "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, "Invalid QUERY parameter '" #NAME "'. Expected type is '" #TYPE "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 }
 
 #define OATPP_MACRO_API_CONTROLLER_QUERY_2(TYPE, NAME, QUALIFIER) \
 const 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 + "'"); \
+if(!__param_str_val_##NAME) \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, \
+                                                         oatpp::String("Missing QUERY parameter '") + QUALIFIER + "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 } \
 bool __param_validation_check_##NAME; \
 const auto& NAME = ApiController::TypeInterpretation<TYPE>::fromString(#TYPE, __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 "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, \
+                                                         oatpp::String("Invalid QUERY parameter '") + \
+                                                         QUALIFIER + \
+                                                         "'. Expected type is '" #TYPE "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 }
 
 #define OATPP_MACRO_API_CONTROLLER_QUERY_3(TYPE, NAME, QUALIFIER, DEFAULT) \
@@ -193,10 +216,12 @@ if(__param_str_val_##NAME) { \
   bool __param_validation_check_##NAME; \
   NAME = ApiController::TypeInterpretation<TYPE>::fromString(#TYPE, __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 "'"); \
+    auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, \
+                                                           oatpp::String("Invalid QUERY parameter '") + \
+                                                           QUALIFIER + \
+                                                           "'. Expected type is '" #TYPE "'"); \
+    auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
   } \
 }
 
@@ -236,12 +261,16 @@ if(getDefaultObjectMapper()) { \
 
 #define OATPP_MACRO_API_CONTROLLER_BODY_DTO(TYPE, PARAM_LIST) \
 if(!getDefaultObjectMapper()) { \
-  return ApiController::handleError(Status::CODE_500, "ObjectMapper was NOT set. Can't deserialize the request body."); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_500, "ObjectMapper was NOT set. Can't deserialize the request body."); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 } \
 const auto& OATPP_MACRO_FIRSTARG PARAM_LIST = \
 __request->readBodyToDto<TYPE>(getDefaultObjectMapper().get()); \
 if(!OATPP_MACRO_FIRSTARG PARAM_LIST) { \
-  return ApiController::handleError(Status::CODE_400, "Missing valid body parameter '" OATPP_MACRO_FIRSTARG_STR PARAM_LIST "'"); \
+  auto httpError = oatpp::web::protocol::http::HttpError(Status::CODE_400, "Missing valid body parameter '" OATPP_MACRO_FIRSTARG_STR PARAM_LIST "'"); \
+  auto eptr = std::make_exception_ptr(httpError); \
+  return ApiController::handleError(eptr); \
 }
 
 // __INFO

+ 1 - 5
src/oatpp/core/async/Error.cpp

@@ -27,11 +27,7 @@
 namespace oatpp { namespace async {
 
 Error::Error(const std::string& what)
-  : m_what(what)
+    : runtime_error(what)
 {}
 
-const char* Error::what() const {
-  return m_what.c_str();
-}
-
 }}

+ 2 - 15
src/oatpp/core/async/Error.hpp

@@ -33,27 +33,14 @@ namespace oatpp { namespace async {
 /**
  * Class to hold and communicate errors between Coroutines
  */
-class Error : public oatpp::base::Countable {
-private:
-  std::string m_what;
+class Error : public std::runtime_error, public oatpp::base::Countable {
 public:
 
   /**
    * Constructor.
    * @param what - error explanation.
    */
-  Error(const std::string& what);
-
-  /**
-   * Virtual destructor.
-   */
-  virtual ~Error() = default;
-
-  /**
-   * Error explanation.
-   * @return
-   */
-  const char* what() const;
+  explicit Error(const std::string& what);
 
   /**
    * Check if error belongs to specified class.

+ 1 - 0
src/oatpp/web/protocol/http/Http.cpp

@@ -70,6 +70,7 @@ const Status Status::CODE_414(414, "Request-URI Too Large");
 const Status Status::CODE_415(415, "Unsupported Media Type");
 const Status Status::CODE_416(416, "Requested Range Not Satisfiable");
 const Status Status::CODE_417(417, "Expectation Failed");
+const Status Status::CODE_418(418, "I'm a Teapot");
 const Status Status::CODE_422(422, "Unprocessable Entity");
 const Status Status::CODE_423(423, "Locked");
 const Status Status::CODE_424(424, "Failed Dependency");

+ 5 - 0
src/oatpp/web/protocol/http/Http.hpp

@@ -246,6 +246,11 @@ public:
    */
   static const Status CODE_417;// Expectation Failed
 
+  /**
+   * I'm a Teapot (rfc7168 2.3.3)
+   */
+  static const Status CODE_418;// I'm a teapot
+
   /**
    * Unprocessable Entity.
    */

+ 20 - 23
src/oatpp/web/server/HttpProcessor.cpp

@@ -107,21 +107,17 @@ HttpProcessor::processNextRequest(ProcessingResources& resources,
       << "', URL: '" << request->getStartingLine().path.toString() << "'";
 
       connectionState = ConnectionState::CLOSING;
-      return resources.components->errorHandler->handleError(protocol::http::Status::CODE_404, ss.toString());
+      oatpp::web::protocol::http::HttpError error(protocol::http::Status::CODE_404, ss.toString());
+      auto ptr = std::make_exception_ptr(error);
+      return resources.components->errorHandler->handleError(ptr);
 
     }
 
     request->setPathVariables(route.getMatchMap());
     return route.getEndpoint()->handle(request);
 
-  } catch (oatpp::web::protocol::http::HttpError& error) {
-    response = resources.components->errorHandler->handleError(error.getInfo().status, error.getMessage(), error.getHeaders());
-    connectionState = ConnectionState::CLOSING;
-  } catch (std::exception& error) {
-    response = resources.components->errorHandler->handleError(protocol::http::Status::CODE_500, error.what());
-    connectionState = ConnectionState::CLOSING;
   } catch (...) {
-    response = resources.components->errorHandler->handleError(protocol::http::Status::CODE_500, "Unhandled Error");
+    response = resources.components->errorHandler->handleError(std::current_exception());
     connectionState = ConnectionState::CLOSING;
   }
 
@@ -143,7 +139,9 @@ HttpProcessor::ConnectionState HttpProcessor::processNextRequest(ProcessingResou
   std::shared_ptr<protocol::http::outgoing::Response> response;
 
   if(error.status.code != 0) {
-    response = resources.components->errorHandler->handleError(error.status, "Invalid Request Headers");
+    oatpp::web::protocol::http::HttpError httpError(error.status, "Invalid Request Headers");
+    auto eptr = std::make_exception_ptr(httpError);
+    response = resources.components->errorHandler->handleError(eptr);
     connectionState = ConnectionState::CLOSING;
   } else {
 
@@ -160,19 +158,15 @@ HttpProcessor::ConnectionState HttpProcessor::processNextRequest(ProcessingResou
       for (auto& interceptor : resources.components->responseInterceptors) {
         response = interceptor->intercept(request, response);
         if (!response) {
-          response = resources.components->errorHandler->handleError(
-            protocol::http::Status::CODE_500,
-            "Response Interceptor returned an Invalid Response - 'null'"
-          );
+          oatpp::web::protocol::http::HttpError httpError(protocol::http::Status::CODE_500, "Response Interceptor returned an Invalid Response - 'null'");
+          auto eptr = std::make_exception_ptr(httpError);
+          response = resources.components->errorHandler->handleError(eptr);
           connectionState = ConnectionState::CLOSING;
         }
       }
 
     } catch (...) {
-      response = resources.components->errorHandler->handleError(
-        protocol::http::Status::CODE_500,
-        "Unhandled Error in Response Interceptor"
-      );
+      response = resources.components->errorHandler->handleError(std::current_exception());
       connectionState = ConnectionState::CLOSING;
     }
 
@@ -327,7 +321,9 @@ oatpp::async::Action HttpProcessor::Coroutine::onHeadersParsed(const RequestHead
     data::stream::BufferOutputStream ss;
     ss << "No mapping for HTTP-method: '" << headersReadResult.startingLine.method.toString()
        << "', URL: '" << headersReadResult.startingLine.path.toString() << "'";
-    m_currentResponse = m_components->errorHandler->handleError(protocol::http::Status::CODE_404, ss.toString());
+    oatpp::web::protocol::http::HttpError error(protocol::http::Status::CODE_404, ss.toString());
+    auto eptr = std::make_exception_ptr(error);
+    m_currentResponse = m_components->errorHandler->handleError(eptr);
     m_connectionState = ConnectionState::CLOSING;
     return yieldTo(&HttpProcessor::Coroutine::onResponseFormed);
   }
@@ -352,10 +348,9 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::onResponseFormed() {
   for(auto& interceptor : m_components->responseInterceptors) {
     m_currentResponse = interceptor->intercept(m_currentRequest, m_currentResponse);
     if(!m_currentResponse) {
-      m_currentResponse = m_components->errorHandler->handleError(
-        protocol::http::Status::CODE_500,
-        "Response Interceptor returned an Invalid Response - 'null'"
-      );
+      oatpp::web::protocol::http::HttpError error(protocol::http::Status::CODE_500, "Response Interceptor returned an Invalid Response - 'null'");
+      auto eptr = std::make_exception_ptr(error);
+      m_currentResponse = m_components->errorHandler->handleError(eptr);
     }
   }
 
@@ -430,7 +425,9 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::handleError(Error* er
       return error;
     }
 
-    m_currentResponse = m_components->errorHandler->handleError(protocol::http::Status::CODE_500, error->what());
+    oatpp::web::protocol::http::HttpError httpError(protocol::http::Status::CODE_500, error->what());
+    auto eptr = std::make_exception_ptr(httpError);
+    m_currentResponse = m_components->errorHandler->handleError(eptr);
     return yieldTo(&HttpProcessor::Coroutine::onResponseFormed);
 
   }

+ 8 - 3
src/oatpp/web/server/api/ApiController.cpp

@@ -23,6 +23,7 @@
  ***************************************************************************/
 
 #include "ApiController.hpp"
+
 #include "oatpp/web/server/handler/ErrorHandler.hpp"
 
 namespace oatpp { namespace web { namespace server { namespace api {
@@ -49,13 +50,17 @@ std::shared_ptr<ApiController::RequestHandler> ApiController::getEndpointHandler
 
 void ApiController::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){
   m_errorHandler = errorHandler;
+  if(!m_errorHandler) {
+    m_errorHandler = handler::DefaultErrorHandler::createShared();
+  }
 }
 
-std::shared_ptr<ApiController::OutgoingResponse> ApiController::handleError(const Status& status, const oatpp::String& message) const {
+std::shared_ptr<ApiController::OutgoingResponse> ApiController::handleError(const std::exception_ptr& exceptionPtr) const {
   if(m_errorHandler) {
-    return m_errorHandler->handleError(status, message);
+    return m_errorHandler->handleError(exceptionPtr);
   }
-  throw oatpp::web::protocol::http::HttpError(status, message);
+
+  return nullptr;
 }
 
 void ApiController::setDefaultAuthorizationHandler(const std::shared_ptr<handler::AuthorizationHandler>& authorizationHandler){

+ 17 - 23
src/oatpp/web/server/api/ApiController.hpp

@@ -236,7 +236,8 @@ protected:
       }
 
       async::Action handleError(async::Error* error) override {
-        auto response = m_handler->m_controller->m_errorHandler->handleError(protocol::http::Status::CODE_500, error->what());
+        auto eptr = std::make_exception_ptr(*error);
+        auto response = m_handler->m_controller->m_errorHandler->handleError(eptr);
         return this->_return(response);
       }
 
@@ -262,27 +263,22 @@ protected:
 
       if(m_method == nullptr) {
         if(m_methodAsync == nullptr) {
-          return m_controller->handleError(Status::CODE_500, "[ApiController]: Error. Handler method is nullptr.");
+          throw protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Handler method is nullptr.");;
         }
-        return m_controller->handleError(Status::CODE_500, "[ApiController]: Error. Non-async call to async endpoint.");
+        throw protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Non-async call to async endpoint.");;
       }
 
-      if(m_controller->m_errorHandler) {
-
-        try {
-          return (m_controller->*m_method)(request);
-        } catch (oatpp::web::protocol::http::HttpError& error) {
-          return m_controller->m_errorHandler->handleError(error.getInfo().status, error.getMessage(), error.getHeaders());
-        } catch (std::exception& error) {
-          return m_controller->m_errorHandler->handleError(protocol::http::Status::CODE_500, error.what());
-        } catch (...) {
-          return m_controller->m_errorHandler->handleError(protocol::http::Status::CODE_500, "Unknown error");
+      try {
+        return (m_controller->*m_method)(request);
+      } catch (...) {
+        auto response = m_controller->handleError(std::current_exception());
+        if(response != nullptr) {
+          return response;
         }
 
+        throw;
       }
-
-      return (m_controller->*m_method)(request);
-
+      
     }
     
     oatpp::async::CoroutineStarterForResult<const std::shared_ptr<OutgoingResponse>&>
@@ -383,18 +379,16 @@ public:
   const Endpoints& getEndpoints();
 
   /**
-   * [under discussion]
-   * Set error handler to handle calls to handleError
+   * Set error handler to handle errors that occur during the endpoint's execution
    */
   void setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler);
 
   /**
-   * [under discussion]
-   * Do not use it directly. This method is under discussion.
-   * Currently returns Response created by registered ErrorHandler or returns Response created by DefaultErrorHandler::handleDefaultError
-   * Notice: Does not throw the Error anymore, error-response has to be returned by the caller!
+   * Handle the exception using the registered ErrorHandler or if no handler has been set, uses the DefaultErrorHandler::handleError
+   * @note Does not rethrow an exception anymore, OutgoingResponse has to be returned by the caller!
+   * @note If this handler fails to handle the exception, it will be handled by the connection handlers ErrorHandler.
    */
-  std::shared_ptr<OutgoingResponse> handleError(const Status& status, const oatpp::String& message) const;
+  std::shared_ptr<OutgoingResponse> handleError(const std::exception_ptr& exceptionPtr) const;
 
   /**
    * [under discussion]

+ 32 - 0
src/oatpp/web/server/handler/ErrorHandler.cpp

@@ -28,9 +28,36 @@
 
 namespace oatpp { namespace web { namespace server { namespace handler {
 
+std::shared_ptr<protocol::http::outgoing::Response> ErrorHandler::handleError(const std::exception_ptr& exceptionPtr) {
+
+  std::shared_ptr<protocol::http::outgoing::Response> response;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+  /* Default impl for backwards compatibility until the deprecated methods are removed */
+  try {
+    if(exceptionPtr) {
+      std::rethrow_exception(exceptionPtr);
+    }
+  } catch (protocol::http::HttpError& httpError) {
+    response = handleError(httpError.getInfo().status, httpError.getMessage(), httpError.getHeaders());
+  } catch (std::exception& error) {
+    response = handleError(protocol::http::Status::CODE_500, error.what());
+  } catch (...) {
+    response = handleError(protocol::http::Status::CODE_500, "An unknown error has occurred");
+  }
+#pragma GCC diagnostic pop
+
+  return response;
+
+}
+
 std::shared_ptr<protocol::http::outgoing::Response> ErrorHandler::handleError(const protocol::http::Status& status, const oatpp::String& message) {
   Headers headers;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
   return handleError(status, message, headers);
+#pragma GCC diagnostic pop
 }
 
 std::shared_ptr<protocol::http::outgoing::Response>
@@ -55,4 +82,9 @@ DefaultErrorHandler::handleError(const oatpp::web::protocol::http::Status &statu
 
 }
 
+std::shared_ptr<protocol::http::outgoing::Response>
+DefaultErrorHandler::handleError(const std::exception_ptr& error) {
+  return ErrorHandler::handleError(error);
+}
+
 }}}}

+ 13 - 3
src/oatpp/web/server/handler/ErrorHandler.hpp

@@ -42,10 +42,17 @@ public:
   typedef web::protocol::http::Headers Headers;
 public:
   /**
-   * Virtual destructor since the class is ment to be derived from.
+   * Virtual destructor since the class is meant to be derived from.
    * */
   virtual ~ErrorHandler() = default;
 
+   /**
+   * Implement this method!
+   * @param error - &std::exception;.
+   * @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;.
+   */
+  virtual std::shared_ptr<protocol::http::outgoing::Response> handleError(const std::exception_ptr& exceptionPtr) = 0;
+
   /**
    * Implement this method!
    * @param status - &id:oatpp::web::protocol::http::Status;.
@@ -53,6 +60,7 @@ public:
    * @param Headers - &id:oatpp::web::protocol::http::Headers;
    * @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;.
    */
+  [[deprecated]]
   virtual
   std::shared_ptr<protocol::http::outgoing::Response>
   handleError(const protocol::http::Status& status, const oatpp::String& message, const Headers& headers) = 0;
@@ -63,6 +71,7 @@ public:
    * @param message - &id:oatpp::String;.
    * @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;.
    */
+  [[deprecated]]
   std::shared_ptr<protocol::http::outgoing::Response> handleError(const protocol::http::Status& status, const oatpp::String& message);
   
 };
@@ -75,8 +84,7 @@ public:
   /**
    * Constructor.
    */
-  DefaultErrorHandler()
-  {}
+  DefaultErrorHandler() = default;
 public:
 
   /**
@@ -87,6 +95,8 @@ public:
     return std::make_shared<DefaultErrorHandler>();
   }
 
+  std::shared_ptr<protocol::http::outgoing::Response> handleError(const std::exception_ptr& error) override;
+
   /**
    * Implementation of &l:ErrorHandler::handleError ();
    * @param status - &id:oatpp::web::protocol::http::Status;.

+ 1 - 0
test/CMakeLists.txt

@@ -109,6 +109,7 @@ add_executable(oatppAllTests
         oatpp/web/app/DTOs.hpp
         oatpp/web/app/ControllerWithInterceptors.hpp
         oatpp/web/app/ControllerWithInterceptorsAsync.hpp
+        oatpp/web/app/ControllerWithErrorHandler.hpp
 )
 
 target_link_libraries(oatppAllTests PRIVATE oatpp PRIVATE oatpp-test)

+ 9 - 0
test/oatpp/web/FullTest.cpp

@@ -27,6 +27,7 @@
 #include "oatpp/web/app/Client.hpp"
 
 #include "oatpp/web/app/ControllerWithInterceptors.hpp"
+#include "oatpp/web/app/ControllerWithErrorHandler.hpp"
 #include "oatpp/web/app/Controller.hpp"
 #include "oatpp/web/app/BasicAuthorizationController.hpp"
 #include "oatpp/web/app/BearerAuthorizationController.hpp"
@@ -143,6 +144,7 @@ void FullTest::onRun() {
 
   runner.addController(app::Controller::createShared());
   runner.addController(app::ControllerWithInterceptors::createShared());
+  runner.addController(app::ControllerWithErrorHandler::createShared());
   runner.addController(app::DefaultBasicAuthorizationController::createShared());
   runner.addController(app::BasicAuthorizationController::createShared());
   runner.addController(app::BearerAuthorizationController::createShared());
@@ -487,6 +489,13 @@ void FullTest::onRun() {
         OATPP_ASSERT(value == "Hello World!!!");
       }
 
+      { // test controller's error handler catches
+        auto response = client->getCaughtError(connection);
+        OATPP_ASSERT(response->getStatusCode() == 418);
+        auto value = response->readBodyToString();
+        OATPP_ASSERT(value == "Controller With Errors!");
+      }
+
       { // test header replacement
         auto response = client->getInterceptors(connection);
         OATPP_ASSERT(response->getStatusCode() == 200);

+ 2 - 0
test/oatpp/web/app/Client.hpp

@@ -76,6 +76,8 @@ public:
 
   API_CALL("GET", "test/interceptors", getInterceptors)
 
+  API_CALL("GET", "test/errorhandling", getCaughtError)
+
   API_CALL_HEADERS(getDefaultHeaders1) {
     headers.put("X-DEFAULT", "hello_1");
   }

+ 105 - 0
test/oatpp/web/app/ControllerWithErrorHandler.hpp

@@ -0,0 +1,105 @@
+/***************************************************************************
+ *
+ * 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_ControllerWithErrorHandler_hpp
+#define oatpp_test_web_app_ControllerWithErrorHandler_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 http = oatpp::web::protocol::http;
+
+/**
+ * Custom Error Handler.
+ */
+class CustomErrorHandler : public oatpp::base::Countable, public oatpp::web::server::handler::ErrorHandler {
+public:
+  /**
+   * Constructor.
+   */
+  CustomErrorHandler() = default;
+public:
+
+  /**
+   * Create shared DefaultErrorHandler.
+   * @return - `std::shared_ptr` to DefaultErrorHandler.
+   */
+  static std::shared_ptr<CustomErrorHandler> createShared() {
+    return std::make_shared<CustomErrorHandler>();
+  }
+
+  std::shared_ptr<http::outgoing::Response> handleError(const std::exception_ptr& error) override {
+    try {
+      std::rethrow_exception(error);
+    } catch(const std::runtime_error& e) {
+      return oatpp::web::protocol::http::outgoing::ResponseFactory::createResponse(http::Status::CODE_418, e.what());
+    } catch(...) {
+      throw;
+    }
+  }
+
+  std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> handleError(const http::Status& status, const oatpp::String& message, const Headers& headers) override {
+    throw std::logic_error("Function not implemented");
+  }
+
+};
+
+class ControllerWithErrorHandler : public oatpp::web::server::api::ApiController {
+private:
+  static constexpr const char* TAG = "test::web::app::ControllerWithErrorHandler";
+public:
+  explicit ControllerWithErrorHandler(const std::shared_ptr<ObjectMapper>& objectMapper)
+      : oatpp::web::server::api::ApiController(objectMapper)
+  {
+    setErrorHandler(CustomErrorHandler::createShared());
+  }
+public:
+
+  static std::shared_ptr<ControllerWithErrorHandler> createShared(const std::shared_ptr<ObjectMapper>& objectMapper = OATPP_GET_COMPONENT(std::shared_ptr<ObjectMapper>)){
+    return std::make_shared<ControllerWithErrorHandler>(objectMapper);
+  }
+
+#include OATPP_CODEGEN_BEGIN(ApiController)
+
+  ENDPOINT("GET", "test/errorhandling", errorCaught,
+           REQUEST(std::shared_ptr<IncomingRequest>, request))
+  {
+    throw std::runtime_error("Controller With Errors!");
+  }
+
+
+#include OATPP_CODEGEN_END(ApiController)
+
+};
+
+}}}}
+
+#endif /* oatpp_test_web_app_ControllerWithErrorHandler_hpp */