diff --git a/src/oatpp/codegen/api_controller/base_define.hpp b/src/oatpp/codegen/api_controller/base_define.hpp index 99edb18b..be0f726f 100644 --- a/src/oatpp/codegen/api_controller/base_define.hpp +++ b/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::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::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::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::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::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::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::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(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 diff --git a/src/oatpp/core/async/Error.cpp b/src/oatpp/core/async/Error.cpp index 19aead7d..3901f874 100644 --- a/src/oatpp/core/async/Error.cpp +++ b/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(); -} - }} \ No newline at end of file diff --git a/src/oatpp/core/async/Error.hpp b/src/oatpp/core/async/Error.hpp index 2a56852a..6a82e1b9 100644 --- a/src/oatpp/core/async/Error.hpp +++ b/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. diff --git a/src/oatpp/web/protocol/http/Http.cpp b/src/oatpp/web/protocol/http/Http.cpp index f044398e..69eb0fcd 100644 --- a/src/oatpp/web/protocol/http/Http.cpp +++ b/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"); diff --git a/src/oatpp/web/protocol/http/Http.hpp b/src/oatpp/web/protocol/http/Http.hpp index 2c5979dd..93c2307a 100644 --- a/src/oatpp/web/protocol/http/Http.hpp +++ b/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. */ diff --git a/src/oatpp/web/server/HttpProcessor.cpp b/src/oatpp/web/server/HttpProcessor.cpp index 50e6703f..94116ab6 100644 --- a/src/oatpp/web/server/HttpProcessor.cpp +++ b/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 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); } diff --git a/src/oatpp/web/server/api/ApiController.cpp b/src/oatpp/web/server/api/ApiController.cpp index aad58b21..beffc3b1 100644 --- a/src/oatpp/web/server/api/ApiController.cpp +++ b/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::getEndpointHandler void ApiController::setErrorHandler(const std::shared_ptr& errorHandler){ m_errorHandler = errorHandler; + if(!m_errorHandler) { + m_errorHandler = handler::DefaultErrorHandler::createShared(); + } } -std::shared_ptr ApiController::handleError(const Status& status, const oatpp::String& message) const { +std::shared_ptr 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& authorizationHandler){ diff --git a/src/oatpp/web/server/api/ApiController.hpp b/src/oatpp/web/server/api/ApiController.hpp index caad04dc..0703338b 100644 --- a/src/oatpp/web/server/api/ApiController.hpp +++ b/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&> @@ -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& 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 handleError(const Status& status, const oatpp::String& message) const; + std::shared_ptr handleError(const std::exception_ptr& exceptionPtr) const; /** * [under discussion] diff --git a/src/oatpp/web/server/handler/ErrorHandler.cpp b/src/oatpp/web/server/handler/ErrorHandler.cpp index 82611b5e..2f8baa89 100644 --- a/src/oatpp/web/server/handler/ErrorHandler.cpp +++ b/src/oatpp/web/server/handler/ErrorHandler.cpp @@ -28,9 +28,36 @@ namespace oatpp { namespace web { namespace server { namespace handler { +std::shared_ptr ErrorHandler::handleError(const std::exception_ptr& exceptionPtr) { + + std::shared_ptr 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 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 @@ -55,4 +82,9 @@ DefaultErrorHandler::handleError(const oatpp::web::protocol::http::Status &statu } +std::shared_ptr +DefaultErrorHandler::handleError(const std::exception_ptr& error) { + return ErrorHandler::handleError(error); +} + }}}} diff --git a/src/oatpp/web/server/handler/ErrorHandler.hpp b/src/oatpp/web/server/handler/ErrorHandler.hpp index af79d3d2..d94d56ce 100644 --- a/src/oatpp/web/server/handler/ErrorHandler.hpp +++ b/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 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 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 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(); } + std::shared_ptr handleError(const std::exception_ptr& error) override; + /** * Implementation of &l:ErrorHandler::handleError (); * @param status - &id:oatpp::web::protocol::http::Status;. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f7586f74..ee3c999e 100644 --- a/test/CMakeLists.txt +++ b/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) diff --git a/test/oatpp/web/FullTest.cpp b/test/oatpp/web/FullTest.cpp index bf993304..6172379d 100644 --- a/test/oatpp/web/FullTest.cpp +++ b/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); diff --git a/test/oatpp/web/app/Client.hpp b/test/oatpp/web/app/Client.hpp index 6abbf3f9..a6ab7224 100644 --- a/test/oatpp/web/app/Client.hpp +++ b/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"); } diff --git a/test/oatpp/web/app/ControllerWithErrorHandler.hpp b/test/oatpp/web/app/ControllerWithErrorHandler.hpp new file mode 100644 index 00000000..36aa13c0 --- /dev/null +++ b/test/oatpp/web/app/ControllerWithErrorHandler.hpp @@ -0,0 +1,105 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * 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 + +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 createShared() { + return std::make_shared(); + } + + std::shared_ptr 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 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) + : oatpp::web::server::api::ApiController(objectMapper) + { + setErrorHandler(CustomErrorHandler::createShared()); + } +public: + + static std::shared_ptr createShared(const std::shared_ptr& objectMapper = OATPP_GET_COMPONENT(std::shared_ptr)){ + return std::make_shared(objectMapper); + } + +#include OATPP_CODEGEN_BEGIN(ApiController) + + ENDPOINT("GET", "test/errorhandling", errorCaught, + REQUEST(std::shared_ptr, request)) + { + throw std::runtime_error("Controller With Errors!"); + } + + +#include OATPP_CODEGEN_END(ApiController) + +}; + +}}}} + +#endif /* oatpp_test_web_app_ControllerWithErrorHandler_hpp */