Merge pull request #565 from JeremyGuinn/error-handler-enhancements

Add new virtual handleError method to handle the original error
This commit is contained in:
Leonid Stryzhevskyi 2022-02-24 00:58:05 +02:00 committed by GitHub
commit 0a3969b157
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 274 additions and 101 deletions

View File

@ -71,27 +71,34 @@ 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 "'"); \
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, \
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, \
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, \
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, \
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, \
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

View File

@ -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();
}
}}

View File

@ -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.

View File

@ -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");

View File

@ -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.
*/

View File

@ -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);
}

View File

@ -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){

View File

@ -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");
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]

View File

@ -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);
}
}}}}

View File

@ -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;.

View File

@ -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)

View File

@ -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);

View File

@ -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");
}

View File

@ -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 */