Merge pull request #946 from oatpp/better_error_handling

Better error handling
This commit is contained in:
Leonid Stryzhevskyi 2024-06-03 17:53:35 -07:00 committed by GitHub
commit 39b4e00bdc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 441 additions and 245 deletions

View File

@ -13,6 +13,7 @@ Contents:
- [Remapper](#remapper) - [Remapper](#remapper)
- [oatpp::web::mime::ContentMappers](#oatppwebmimecontentmappers) - [oatpp::web::mime::ContentMappers](#oatppwebmimecontentmappers)
- [New OATPP_LOGx format](#new-oatpp_logx-format) - [New OATPP_LOGx format](#new-oatpp_logx-format)
- [Better error handling](#better-error-handling)
- [Restructuring](#restructuring) - [Restructuring](#restructuring)
@ -172,6 +173,66 @@ Instead of old formatting "%s", "%d", "%f" use "{}" for any variable type:
OATPP_LOGd("MyController", "User: name={}, age={}", user->name, user->age) OATPP_LOGd("MyController", "User: name={}, age={}", user->name, user->age)
``` ```
## Better error handling
Fixed error handling in the `ApiController` and `ConnectionHandler`.
The new recommended way to implement custom error handler:
```cpp
/**
* Extend the DefaultErrorHandling class by overriding the renderError() method instead of the handleError() method.
* You can still override the handleError() method, but in most cases, it isn't necessary.
*/
class ErrorHandler : public oatpp::web::server::handler::DefaultErrorHandler {
private:
/* mappers to map response according to 'Accept' header and available mappers */
std::shared_ptr<oatpp::web::mime::ContentMappers> m_mappers;
public:
ErrorHandler(const std::shared_ptr<oatpp::web::mime::ContentMappers>& mappers)
: m_mappers(mappers)
{}
std::shared_ptr<OutgoingResponse> renderError(const HttpServerErrorStacktrace& stacktrace) override {
/* create ErrorDto */
auto error = ErrorDto::createShared();
error->code = stacktrace.status.code;
error->stack = {}; // initialize stack as empty list
/* push all items of stacktrace */
for(auto& s : stacktrace.stack) {
error->stack->push_back(s);
}
/* get all variants of acceptable mime-types from request */
std::vector<oatpp::String> acceptable;
if(stacktrace.request) {
acceptable = stacktrace.request->getHeaderValues("Accept");
}
/* select mapper based on provided preferences */
auto mapper = m_mappers->selectMapper(acceptable);
if(!mapper) {
mapper = m_mappers->getDefaultMapper();
}
/* create response object */
auto response = ResponseFactory::createResponse(stacktrace.status, error, mapper);
/* put all error related headers */
for(const auto& pair : stacktrace.headers.getAll()) {
response->putHeader(pair.first.toString(), pair.second.toString());
}
/* return response */
return response;
}
};
```
## Restructuring ## Restructuring

View File

@ -289,6 +289,8 @@ add_library(oatpp
oatpp/web/server/HttpRequestHandler.hpp oatpp/web/server/HttpRequestHandler.hpp
oatpp/web/server/HttpRouter.cpp oatpp/web/server/HttpRouter.cpp
oatpp/web/server/HttpRouter.hpp oatpp/web/server/HttpRouter.hpp
oatpp/web/server/HttpServerError.cpp
oatpp/web/server/HttpServerError.hpp
oatpp/web/server/api/ApiController.cpp oatpp/web/server/api/ApiController.cpp
oatpp/web/server/api/ApiController.hpp oatpp/web/server/api/ApiController.hpp
oatpp/web/server/api/Endpoint.cpp oatpp/web/server/api/Endpoint.cpp

View File

@ -260,7 +260,7 @@ Action CoroutineHandle::takeAction(Action&& action) {
_CP = action.m_data.coroutine; _CP = action.m_data.coroutine;
_FP = &AbstractCoroutine::act; _FP = &AbstractCoroutine::act;
action.m_type = Action::TYPE_NONE; action.m_type = Action::TYPE_NONE;
return std::forward<oatpp::async::Action>(action); return std::move(action);
} }
case Action::TYPE_FINISH: { case Action::TYPE_FINISH: {
@ -277,7 +277,7 @@ Action CoroutineHandle::takeAction(Action&& action) {
case Action::TYPE_YIELD_TO: { case Action::TYPE_YIELD_TO: {
_FP = action.m_data.fptr; _FP = action.m_data.fptr;
//break; //break;
return std::forward<oatpp::async::Action>(action); return std::move(action);
} }
// case Action::TYPE_REPEAT: { // case Action::TYPE_REPEAT: {
@ -289,7 +289,12 @@ Action CoroutineHandle::takeAction(Action&& action) {
// } // }
case Action::TYPE_ERROR: { case Action::TYPE_ERROR: {
Action newAction = _CP->handleError(action.m_data.error); Action newAction;
try {
newAction = _CP->handleError(action.m_data.error);
} catch (...) {
newAction = new Error(std::current_exception());
}
if (newAction.m_type == Action::TYPE_ERROR) { if (newAction.m_type == Action::TYPE_ERROR) {
AbstractCoroutine* savedCP = _CP; AbstractCoroutine* savedCP = _CP;
@ -303,7 +308,7 @@ Action CoroutineHandle::takeAction(Action&& action) {
if(_CP == nullptr) { if(_CP == nullptr) {
delete action.m_data.error; delete action.m_data.error;
action.m_type = Action::TYPE_NONE; action.m_type = Action::TYPE_NONE;
return std::forward<oatpp::async::Action>(action); return std::move(action);
} }
} else { } else {
action = std::move(newAction); action = std::move(newAction);
@ -313,7 +318,7 @@ Action CoroutineHandle::takeAction(Action&& action) {
} }
default: default:
return std::forward<oatpp::async::Action>(action); return std::move(action);
} }
@ -329,10 +334,8 @@ Action CoroutineHandle::takeAction(Action&& action) {
Action CoroutineHandle::iterate() { Action CoroutineHandle::iterate() {
try { try {
return _CP->call(_FP); return _CP->call(_FP);
} catch (std::exception& e) {
return new Error(e.what());
} catch (...) { } catch (...) {
return new Error("[oatpp::async::CoroutineHandle::iterate()]: Error. Unknown Exception."); return new Error(std::current_exception());
} }
} }

View File

@ -26,8 +26,23 @@
namespace oatpp { namespace async { namespace oatpp { namespace async {
Error::Error(const std::string& what) Error::Error(const std::string& message)
: runtime_error(what) : m_message(message)
{} {
}
Error::Error(const std::exception_ptr& exceptionPtr)
: m_message("exception_ptr")
, m_exceptionPtr(exceptionPtr)
{
}
const std::string& Error::what() const {
return m_message;
}
const std::exception_ptr& Error::getExceptionPtr() const {
return m_exceptionPtr;
}
}} }}

View File

@ -26,21 +26,24 @@
#define oatpp_async_Error_hpp #define oatpp_async_Error_hpp
#include "oatpp/base/Countable.hpp" #include "oatpp/base/Countable.hpp"
#include <string>
namespace oatpp { namespace async { namespace oatpp { namespace async {
/** /**
* Class to hold and communicate errors between Coroutines * Class to hold and communicate errors between Coroutines
*/ */
class Error : public std::runtime_error, public oatpp::base::Countable { class Error : public oatpp::base::Countable {
private:
std::string m_message;
std::exception_ptr m_exceptionPtr;
public: public:
/** explicit Error(const std::string& message);
* Constructor. explicit Error(const std::exception_ptr& exceptionPtr);
* @param what - error explanation.
*/ const std::exception_ptr& getExceptionPtr() const;
explicit Error(const std::string& what);
const std::string& what() const;
/** /**
* Check if error belongs to specified class. * Check if error belongs to specified class.

View File

@ -123,7 +123,7 @@ public:
* Get error info. * Get error info.
* @return - error info. * @return - error info.
*/ */
Info getInfo() { Info getInfo() const {
return m_info; return m_info;
} }

View File

@ -419,35 +419,32 @@ public:
/** /**
* Constructor. * Constructor.
* @param info * @param info - Status object
* @param message * @param message - Error message
* @param headers - additional headers recommended for error response.
*/ */
HttpError(const Info& info, const oatpp::String& message) HttpError(const Info& info,
const oatpp::String& message,
const Headers& headers = {})
: protocol::ProtocolError<Status>(info, message) : protocol::ProtocolError<Status>(info, message)
, m_headers(headers)
{} {}
/** /**
* Constructor. * Constructor.
* @param status * @param status - Status object
* @param message * @param message - Error message
* @param headers - additional headers recommended for error response.
*/ */
HttpError(const Status& status, const oatpp::String& message) HttpError(const Status& status,
: protocol::ProtocolError<Status>(Info(0, status), message) const oatpp::String& message,
{} const Headers& headers = {})
/**
* Constructor.
* @param status
* @param message
* @param headers
*/
HttpError(const Status& status, const oatpp::String& message, const Headers& headers)
: protocol::ProtocolError<Status>(Info(0, status), message) : protocol::ProtocolError<Status>(Info(0, status), message)
, m_headers(headers) , m_headers(headers)
{} {}
/** /**
* Get headers * Get headers recommended for error response.
* @return * @return
*/ */
const Headers& getHeaders() const { const Headers& getHeaders() const {
@ -463,7 +460,7 @@ public:
* @param MESSAGE - String message. * @param MESSAGE - String message.
*/ */
#define OATPP_ASSERT_HTTP(COND, STATUS, MESSAGE) \ #define OATPP_ASSERT_HTTP(COND, STATUS, MESSAGE) \
if(!(COND)) { throw oatpp::web::protocol::http::HttpError(STATUS, MESSAGE); } if(!(COND)) { throw oatpp::web::protocol::http::HttpError(STATUS, MESSAGE, {}); }
/** /**
* Collection of HTTP Header constants. * Collection of HTTP Header constants.

View File

@ -90,7 +90,7 @@ std::shared_ptr<AsyncHttpConnectionHandler> AsyncHttpConnectionHandler::createSh
void AsyncHttpConnectionHandler::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){ void AsyncHttpConnectionHandler::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){
m_components->errorHandler = errorHandler; m_components->errorHandler = errorHandler;
if(!m_components->errorHandler) { if(!m_components->errorHandler) {
m_components->errorHandler = handler::DefaultErrorHandler::createShared(); m_components->errorHandler = std::make_shared<handler::DefaultErrorHandler>();
} }
} }

View File

@ -78,7 +78,7 @@ std::shared_ptr<HttpConnectionHandler> HttpConnectionHandler::createShared(const
void HttpConnectionHandler::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){ void HttpConnectionHandler::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){
m_components->errorHandler = errorHandler; m_components->errorHandler = errorHandler;
if(!m_components->errorHandler) { if(!m_components->errorHandler) {
m_components->errorHandler = handler::DefaultErrorHandler::createShared(); m_components->errorHandler = std::make_shared<handler::DefaultErrorHandler>();
} }
} }

View File

@ -24,6 +24,7 @@
#include "HttpProcessor.hpp" #include "HttpProcessor.hpp"
#include "oatpp/web/server/HttpServerError.hpp"
#include "oatpp/web/protocol/http/incoming/SimpleBodyDecoder.hpp" #include "oatpp/web/protocol/http/incoming/SimpleBodyDecoder.hpp"
#include "oatpp/data/stream/BufferStream.hpp" #include "oatpp/data/stream/BufferStream.hpp"
@ -52,7 +53,7 @@ HttpProcessor::Components::Components(const std::shared_ptr<HttpRouter>& pRouter
: Components(pRouter, : Components(pRouter,
nullptr, nullptr,
std::make_shared<oatpp::web::protocol::http::incoming::SimpleBodyDecoder>(), std::make_shared<oatpp::web::protocol::http::incoming::SimpleBodyDecoder>(),
handler::DefaultErrorHandler::createShared(), std::make_shared<handler::DefaultErrorHandler>(),
{}, {},
{}, {},
std::make_shared<Config>()) std::make_shared<Config>())
@ -62,7 +63,7 @@ HttpProcessor::Components::Components(const std::shared_ptr<HttpRouter>& pRouter
: Components(pRouter, : Components(pRouter,
nullptr, nullptr,
std::make_shared<oatpp::web::protocol::http::incoming::SimpleBodyDecoder>(), std::make_shared<oatpp::web::protocol::http::incoming::SimpleBodyDecoder>(),
handler::DefaultErrorHandler::createShared(), std::make_shared<handler::DefaultErrorHandler>(),
{}, {},
{}, {},
pConfig) pConfig)
@ -89,33 +90,33 @@ HttpProcessor::processNextRequest(ProcessingResources& resources,
std::shared_ptr<protocol::http::outgoing::Response> response; std::shared_ptr<protocol::http::outgoing::Response> response;
try{ try {
try {
for(auto& interceptor : resources.components->requestInterceptors) { for (auto &interceptor: resources.components->requestInterceptors) {
response = interceptor->intercept(request); response = interceptor->intercept(request);
if(response) { if (response) {
return response; return response;
}
} }
auto route = resources.components->router->getRoute(request->getStartingLine().method,
request->getStartingLine().path);
if (!route) {
data::stream::BufferOutputStream ss;
ss << "No mapping for HTTP-method: '" << request->getStartingLine().method.toString()
<< "', URL: '" << request->getStartingLine().path.toString() << "'";
throw oatpp::web::protocol::http::HttpError(protocol::http::Status::CODE_404, ss.toString());
}
request->setPathVariables(route.getMatchMap());
return route.getEndpoint()->handle(request);
} catch (...) {
std::throw_with_nested(HttpServerError(request, "Error processing request"));
} }
auto route = resources.components->router->getRoute(request->getStartingLine().method, request->getStartingLine().path);
if(!route) {
data::stream::BufferOutputStream ss;
ss << "No mapping for HTTP-method: '" << request->getStartingLine().method.toString()
<< "', URL: '" << request->getStartingLine().path.toString() << "'";
connectionState = ConnectionState::CLOSING;
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 (...) { } catch (...) {
response = resources.components->errorHandler->handleError(std::current_exception()); response = resources.components->errorHandler->handleError(std::current_exception());
connectionState = ConnectionState::CLOSING; connectionState = ConnectionState::CLOSING;
@ -139,9 +140,9 @@ HttpProcessor::ConnectionState HttpProcessor::processNextRequest(ProcessingResou
std::shared_ptr<protocol::http::outgoing::Response> response; std::shared_ptr<protocol::http::outgoing::Response> response;
if(error.status.code != 0) { if(error.status.code != 0) {
oatpp::web::protocol::http::HttpError httpError(error.status, "Invalid Request Headers"); HttpServerError httpError(nullptr, "Invalid Request Headers");
auto eptr = std::make_exception_ptr(httpError); auto ePtr = std::make_exception_ptr(httpError);
response = resources.components->errorHandler->handleError(eptr); response = resources.components->errorHandler->handleError(ePtr);
connectionState = ConnectionState::CLOSING; connectionState = ConnectionState::CLOSING;
} else { } else {
@ -154,17 +155,18 @@ HttpProcessor::ConnectionState HttpProcessor::processNextRequest(ProcessingResou
response = processNextRequest(resources, request, connectionState); response = processNextRequest(resources, request, connectionState);
try { try {
try {
for (auto& interceptor : resources.components->responseInterceptors) { for (auto &interceptor: resources.components->responseInterceptors) {
response = interceptor->intercept(request, response); response = interceptor->intercept(request, response);
if (!response) { if (!response) {
oatpp::web::protocol::http::HttpError httpError(protocol::http::Status::CODE_500, "Response Interceptor returned an Invalid Response - 'null'"); throw protocol::http::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 (...) {
std::throw_with_nested(HttpServerError(request, "Error processing request during response interception"));
}
} catch (...) { } catch (...) {
response = resources.components->errorHandler->handleError(std::current_exception()); response = resources.components->errorHandler->handleError(std::current_exception());
connectionState = ConnectionState::CLOSING; connectionState = ConnectionState::CLOSING;
@ -284,6 +286,7 @@ HttpProcessor::Coroutine::Coroutine(const std::shared_ptr<Components>& component
, m_inStream(data::stream::InputStreamBufferedProxy::createShared(m_connection.object, std::make_shared<std::string>(data::buffer::IOBuffer::BUFFER_SIZE, 0))) , m_inStream(data::stream::InputStreamBufferedProxy::createShared(m_connection.object, std::make_shared<std::string>(data::buffer::IOBuffer::BUFFER_SIZE, 0)))
, m_connectionState(ConnectionState::ALIVE) , m_connectionState(ConnectionState::ALIVE)
, m_taskListener(taskListener) , m_taskListener(taskListener)
, m_shouldInterceptResponse(false)
{ {
m_taskListener->onTaskStart(m_connection); m_taskListener->onTaskStart(m_connection);
} }
@ -297,6 +300,7 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::act() {
} }
HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::parseHeaders() { HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::parseHeaders() {
m_shouldInterceptResponse = true;
return m_headersReader.readHeadersAsync(m_inStream).callbackTo(&HttpProcessor::Coroutine::onHeadersParsed); return m_headersReader.readHeadersAsync(m_inStream).callbackTo(&HttpProcessor::Coroutine::onHeadersParsed);
} }
@ -322,11 +326,11 @@ oatpp::async::Action HttpProcessor::Coroutine::onHeadersParsed(const RequestHead
data::stream::BufferOutputStream ss; data::stream::BufferOutputStream ss;
ss << "No mapping for HTTP-method: '" << headersReadResult.startingLine.method.toString() ss << "No mapping for HTTP-method: '" << headersReadResult.startingLine.method.toString()
<< "', URL: '" << headersReadResult.startingLine.path.toString() << "'"; << "', URL: '" << headersReadResult.startingLine.path.toString() << "'";
oatpp::web::protocol::http::HttpError error(protocol::http::Status::CODE_404, ss.toString()); throw oatpp::web::protocol::http::HttpError(protocol::http::Status::CODE_404, ss.toString());
auto eptr = std::make_exception_ptr(error); // auto eptr = std::make_exception_ptr(error);
m_currentResponse = m_components->errorHandler->handleError(eptr); // m_currentResponse = m_components->errorHandler->handleError(eptr);
m_connectionState = ConnectionState::CLOSING; // m_connectionState = ConnectionState::CLOSING;
return yieldTo(&HttpProcessor::Coroutine::onResponseFormed); // return yieldTo(&HttpProcessor::Coroutine::onResponseFormed);
} }
m_currentRequest->setPathVariables(m_currentRoute.getMatchMap()); m_currentRequest->setPathVariables(m_currentRoute.getMatchMap());
@ -346,12 +350,16 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::onResponse(const std:
HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::onResponseFormed() { HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::onResponseFormed() {
for(auto& interceptor : m_components->responseInterceptors) { if(m_shouldInterceptResponse) {
m_currentResponse = interceptor->intercept(m_currentRequest, m_currentResponse); m_shouldInterceptResponse = false;
if(!m_currentResponse) { for (auto &interceptor: m_components->responseInterceptors) {
oatpp::web::protocol::http::HttpError error(protocol::http::Status::CODE_500, "Response Interceptor returned an Invalid Response - 'null'"); m_currentResponse = interceptor->intercept(m_currentRequest, m_currentResponse);
auto eptr = std::make_exception_ptr(error); if (!m_currentResponse) {
m_currentResponse = m_components->errorHandler->handleError(eptr); throw oatpp::web::protocol::http::HttpError(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);
}
} }
} }
@ -424,15 +432,37 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::handleError(Error* er
} }
} }
if(m_currentResponse) { // if(m_currentResponse) {
//OATPP_LOGe("[oatpp::web::server::HttpProcessor::Coroutine::handleError()]", "Unhandled error. '{}'. Dropping connection", error->what()) // //OATPP_LOGe("[oatpp::web::server::HttpProcessor::Coroutine::handleError()]", "Unhandled error. '{}'. Dropping connection", error->what())
return error; // return error;
// }
std::exception_ptr ePtr = error->getExceptionPtr();
if(!ePtr) {
ePtr = std::make_exception_ptr(*error);
} }
oatpp::web::protocol::http::HttpError httpError(protocol::http::Status::CODE_500, error->what()); try {
auto eptr = std::make_exception_ptr(httpError); try {
m_currentResponse = m_components->errorHandler->handleError(eptr); std::rethrow_exception(ePtr);
return yieldTo(&HttpProcessor::Coroutine::onResponseFormed); } catch (...) {
std::throw_with_nested(HttpServerError(m_currentRequest, "Error processing async request"));
}
} catch (...) {
ePtr = std::current_exception();
m_currentResponse = m_components->errorHandler->handleError(ePtr);
if (m_currentResponse != nullptr) {
return yieldTo(&HttpProcessor::Coroutine::onResponseFormed);
}
}
return new async::Error(ePtr);
// 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

@ -264,6 +264,8 @@ public:
std::shared_ptr<protocol::http::incoming::Request> m_currentRequest; std::shared_ptr<protocol::http::incoming::Request> m_currentRequest;
std::shared_ptr<protocol::http::outgoing::Response> m_currentResponse; std::shared_ptr<protocol::http::outgoing::Response> m_currentResponse;
TaskProcessingListener* m_taskListener; TaskProcessingListener* m_taskListener;
private:
bool m_shouldInterceptResponse;
public: public:
/** /**

View File

@ -87,7 +87,7 @@ public:
*/ */
virtual std::shared_ptr<OutgoingResponse> handle(const std::shared_ptr<IncomingRequest>& request) { virtual std::shared_ptr<OutgoingResponse> handle(const std::shared_ptr<IncomingRequest>& request) {
(void)request; (void)request;
throw HttpError(Status::CODE_501, "Endpoint not implemented."); throw HttpError(Status::CODE_501, "Endpoint not implemented.", {});
} }
/** /**
@ -99,7 +99,7 @@ public:
virtual oatpp::async::CoroutineStarterForResult<const std::shared_ptr<OutgoingResponse>&> virtual oatpp::async::CoroutineStarterForResult<const std::shared_ptr<OutgoingResponse>&>
handleAsync(const std::shared_ptr<IncomingRequest>& request) { handleAsync(const std::shared_ptr<IncomingRequest>& request) {
(void)request; (void)request;
throw HttpError(Status::CODE_501, "Asynchronous endpoint not implemented."); throw HttpError(Status::CODE_501, "Asynchronous endpoint not implemented.", {});
} }
/** /**

View File

@ -0,0 +1,38 @@
/***************************************************************************
*
* 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.
*
***************************************************************************/
#include "HttpServerError.hpp"
namespace oatpp { namespace web { namespace server {
HttpServerError::HttpServerError(const std::shared_ptr<protocol::http::incoming::Request>& request, const std::string& message)
: std::runtime_error(message)
, m_request(request)
{}
const std::shared_ptr<protocol::http::incoming::Request>& HttpServerError::getRequest() const {
return m_request;
}
}}}

View File

@ -0,0 +1,57 @@
/***************************************************************************
*
* 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_web_server_HttpServerError_hpp
#define oatpp_web_server_HttpServerError_hpp
#include "oatpp/web/protocol/http/incoming/Request.hpp"
namespace oatpp { namespace web { namespace server {
/**
* HttpServerError
*/
class HttpServerError : public std::runtime_error {
private:
std::shared_ptr<protocol::http::incoming::Request> m_request;
public:
/**
* Constructor
* @param request
* @param message
*/
HttpServerError(const std::shared_ptr<protocol::http::incoming::Request>& request, const std::string& message);
/**
* Get request
* @return
*/
const std::shared_ptr<protocol::http::incoming::Request>& getRequest() const;
};
}}}
#endif //oatpp_web_server_HttpServerError_hpp

View File

@ -51,7 +51,7 @@ std::shared_ptr<ApiController::RequestHandler> ApiController::getEndpointHandler
void ApiController::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){ void ApiController::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){
m_errorHandler = errorHandler; m_errorHandler = errorHandler;
if(!m_errorHandler) { if(!m_errorHandler) {
m_errorHandler = handler::DefaultErrorHandler::createShared(); m_errorHandler = std::make_shared<handler::DefaultErrorHandler>();
} }
} }
@ -82,7 +82,7 @@ std::shared_ptr<handler::AuthorizationObject> ApiController::handleDefaultAuthor
return m_defaultAuthorizationHandler->handleAuthorization(authHeader); return m_defaultAuthorizationHandler->handleAuthorization(authHeader);
} }
// If Authorization is not setup on the server then it's 500 // If Authorization is not setup on the server then it's 500
throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "Authorization is not setup."); throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "Authorization is not setup.", {});
} }
const std::shared_ptr<mime::ContentMappers>& ApiController::getContentMappers() const { const std::shared_ptr<mime::ContentMappers>& ApiController::getContentMappers() const {
@ -91,8 +91,7 @@ const std::shared_ptr<mime::ContentMappers>& ApiController::getContentMappers()
// Helper methods // Helper methods
std::shared_ptr<ApiController::OutgoingResponse> ApiController::createResponse(const Status& status, std::shared_ptr<ApiController::OutgoingResponse> ApiController::createResponse(const Status& status, const oatpp::String& str) const {
const oatpp::String& str) const {
return ResponseFactory::createResponse(status, str); return ResponseFactory::createResponse(status, str);
} }

View File

@ -31,6 +31,7 @@
#include "oatpp/web/server/handler/AuthorizationHandler.hpp" #include "oatpp/web/server/handler/AuthorizationHandler.hpp"
#include "oatpp/web/server/handler/ErrorHandler.hpp" #include "oatpp/web/server/handler/ErrorHandler.hpp"
#include "oatpp/web/server/handler/AuthorizationHandler.hpp" #include "oatpp/web/server/handler/AuthorizationHandler.hpp"
#include "oatpp/web/server/HttpServerError.hpp"
#include "oatpp/web/protocol/http/incoming/Response.hpp" #include "oatpp/web/protocol/http/incoming/Response.hpp"
#include "oatpp/web/protocol/http/outgoing/Request.hpp" #include "oatpp/web/protocol/http/outgoing/Request.hpp"
#include "oatpp/web/protocol/http/outgoing/ResponseFactory.hpp" #include "oatpp/web/protocol/http/outgoing/ResponseFactory.hpp"
@ -238,9 +239,28 @@ protected:
} }
async::Action handleError(async::Error* error) override { async::Action handleError(async::Error* error) override {
auto eptr = std::make_exception_ptr(*error);
auto response = m_handler->m_controller->m_errorHandler->handleError(eptr); std::exception_ptr ePtr = error->getExceptionPtr();
return this->_return(response); if(!ePtr) {
ePtr = std::make_exception_ptr(*error);
}
try {
try {
std::rethrow_exception(ePtr);
} catch (...) {
std::throw_with_nested(HttpServerError(m_request, "[ApiController]: Error processing async request"));
}
} catch (...) {
ePtr = std::current_exception();
auto response = m_handler->m_controller->handleError(ePtr);
if (response != nullptr) {
return this->_return(response);
}
}
return new async::Error(ePtr);
} }
}; };
@ -265,20 +285,24 @@ protected:
if(m_method == nullptr) { if(m_method == nullptr) {
if(m_methodAsync == nullptr) { if(m_methodAsync == nullptr) {
throw protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Handler method is nullptr."); throw HttpServerError(request, "[ApiController]: Error. Handler method is nullptr");
} }
throw protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Non-async call to async endpoint."); throw HttpServerError(request, "[ApiController]: Error. Non-async call to async endpoint");
} }
try { try {
return (m_controller->*m_method)(request); try {
return (m_controller->*m_method)(request);
} catch (...) {
std::throw_with_nested(HttpServerError(request, "[ApiController]: Error processing request"));
}
} catch (...) { } catch (...) {
auto response = m_controller->handleError(std::current_exception()); auto ePtr = std::current_exception();
if(response != nullptr) { auto response = m_controller->handleError(ePtr);
if (response != nullptr) {
return response; return response;
} }
std::rethrow_exception(ePtr);
throw;
} }
} }
@ -288,16 +312,16 @@ protected:
if(m_methodAsync == nullptr) { if(m_methodAsync == nullptr) {
if(m_method == nullptr) { if(m_method == nullptr) {
throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Handler method is nullptr."); throw HttpServerError(request, "[ApiController]: Error. Handler method is nullptr");
} }
throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Async call to non-async endpoint."); throw HttpServerError(request, "[ApiController]: Error. Async call to non-async endpoint");
} }
if(m_controller->m_errorHandler) { //if(m_controller->m_errorHandler) {
return ErrorHandlingCoroutine::startForResult(this, request); return ErrorHandlingCoroutine::startForResult(this, request);
} //}
return (m_controller->*m_methodAsync)(request); //return (m_controller->*m_methodAsync)(request);
} }

View File

@ -23,58 +23,71 @@
***************************************************************************/ ***************************************************************************/
#include "./ErrorHandler.hpp" #include "./ErrorHandler.hpp"
#include "oatpp/web/server/HttpServerError.hpp"
#include "oatpp/web/protocol/http/outgoing/BufferBody.hpp" #include "oatpp/web/protocol/http/outgoing/BufferBody.hpp"
namespace oatpp { namespace web { namespace server { namespace handler { namespace oatpp { namespace web { namespace server { namespace handler {
std::shared_ptr<protocol::http::outgoing::Response> ErrorHandler::handleError(const std::exception_ptr& exceptionPtr) { void DefaultErrorHandler::unwrapErrorStack(HttpServerErrorStacktrace& stacktrace, const std::exception& e) {
std::shared_ptr<protocol::http::outgoing::Response> response; stacktrace.stack.emplace_front(e.what());
#pragma GCC diagnostic push if (auto httpServerError = dynamic_cast<const HttpServerError*>(std::addressof(e))) {
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" stacktrace.request = httpServerError->getRequest();
/* Default impl for backwards compatibility until the deprecated methods are removed */ } else if (auto httpError = dynamic_cast<const protocol::http::HttpError*>(std::addressof(e))) {
try { for(auto& h : httpError->getHeaders().getAll_Unsafe()) {
if(exceptionPtr) { stacktrace.headers.putIfNotExists(h.first.toString(), h.second.toString());
std::rethrow_exception(exceptionPtr);
} }
} catch (protocol::http::HttpError& httpError) { stacktrace.status = httpError->getInfo().status;
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; try {
std::rethrow_if_nested(e);
} catch (const std::exception& nestedException) {
unwrapErrorStack(stacktrace, nestedException);
} catch (...) {
}
} }
std::shared_ptr<protocol::http::outgoing::Response> ErrorHandler::handleError(const protocol::http::Status& status, const oatpp::String& message) { std::shared_ptr<protocol::http::outgoing::Response> DefaultErrorHandler::handleError(const std::exception_ptr& error) {
Headers headers;
#pragma GCC diagnostic push HttpServerErrorStacktrace stacktrace;
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" stacktrace.status = protocol::http::Status::CODE_500;
return handleError(status, message, headers);
#pragma GCC diagnostic pop try {
if(error) {
std::rethrow_exception(error);
}
} catch (const std::exception& e) {
unwrapErrorStack(stacktrace, e);
} catch (...) {
}
return renderError(stacktrace);
} }
std::shared_ptr<protocol::http::outgoing::Response> std::shared_ptr<protocol::http::outgoing::Response> DefaultErrorHandler::renderError(const HttpServerErrorStacktrace& stacktrace) {
DefaultErrorHandler::handleError(const oatpp::web::protocol::http::Status &status, const oatpp::String &message, const Headers& headers){
data::stream::BufferOutputStream stream; data::stream::BufferOutputStream stream;
stream << "server=" << protocol::http::Header::Value::SERVER << "\n"; stream << "server=" << protocol::http::Header::Value::SERVER << "\n";
stream << "code=" << status.code << "\n"; stream << "code=" << stacktrace.status.code << "\n";
stream << "description=" << status.description << "\n"; stream << "description=" << stacktrace.status.description << "\n";
stream << "message=" << message << "\n"; stream << "stacktrace:\n";
for(auto& msg : stacktrace.stack) {
stream << " - " << msg << "\n";
}
auto response = protocol::http::outgoing::Response::createShared auto response = protocol::http::outgoing::Response::createShared
(status, protocol::http::outgoing::BufferBody::createShared(stream.toString())); (stacktrace.status, protocol::http::outgoing::BufferBody::createShared(stream.toString()));
response->putHeader(protocol::http::Header::SERVER, protocol::http::Header::Value::SERVER); response->putHeaderIfNotExists(protocol::http::Header::SERVER, protocol::http::Header::Value::SERVER);
response->putHeader(protocol::http::Header::CONNECTION, protocol::http::Header::Value::CONNECTION_CLOSE); response->putHeaderIfNotExists(protocol::http::Header::CONNECTION, protocol::http::Header::Value::CONNECTION_CLOSE);
for(const auto& pair : headers.getAll()) { for(const auto& pair : stacktrace.headers.getAll()) {
response->putHeader_Unsafe(pair.first, pair.second); response->putHeader_Unsafe(pair.first, pair.second);
} }
@ -82,9 +95,4 @@ 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

@ -25,6 +25,7 @@
#ifndef oatpp_web_server_handler_ErrorHandler_hpp #ifndef oatpp_web_server_handler_ErrorHandler_hpp
#define oatpp_web_server_handler_ErrorHandler_hpp #define oatpp_web_server_handler_ErrorHandler_hpp
#include "oatpp/web/protocol/http/incoming/Request.hpp"
#include "oatpp/web/protocol/http/outgoing/Response.hpp" #include "oatpp/web/protocol/http/outgoing/Response.hpp"
#include "oatpp/web/protocol/http/Http.hpp" #include "oatpp/web/protocol/http/Http.hpp"
@ -48,31 +49,10 @@ public:
/** /**
* Implement this method! * Implement this method!
* @param error - &std::exception;. * @param exceptionPtr - `std::exception_ptr`.
* @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;. * @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); 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;.
* @param message - &id:oatpp::String;.
* @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;
/**
* Convenience method to call `handleError` method with no headers.
* @param status - &id:oatpp::web::protocol::http::Status;
* @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);
}; };
@ -81,30 +61,32 @@ public:
*/ */
class DefaultErrorHandler : public oatpp::base::Countable, public ErrorHandler { class DefaultErrorHandler : public oatpp::base::Countable, public ErrorHandler {
public: public:
struct HttpServerErrorStacktrace {
std::shared_ptr<protocol::http::incoming::Request> request;
protocol::http::Status status;
Headers headers;
std::list<oatpp::String> stack;
};
private:
void unwrapErrorStack(HttpServerErrorStacktrace& stacktrace, const std::exception& e);
public:
/** /**
* Constructor. * Constructor.
*/ */
DefaultErrorHandler() = default; DefaultErrorHandler() = default;
public:
/**
* Create shared DefaultErrorHandler.
* @return - `std::shared_ptr` to DefaultErrorHandler.
*/
static std::shared_ptr<DefaultErrorHandler> createShared() {
return std::make_shared<DefaultErrorHandler>();
}
std::shared_ptr<protocol::http::outgoing::Response> handleError(const std::exception_ptr& error) override; std::shared_ptr<protocol::http::outgoing::Response> handleError(const std::exception_ptr& error) override;
/** /**
* Implementation of &l:ErrorHandler::handleError (); * Reimplement this method for custom error rendering.
* @param status - &id:oatpp::web::protocol::http::Status;. * Render error method.
* @param message - &id:oatpp::String;. * @param stacktrace
* @return - &id:oatpp::web::protocol::http::outgoing::Response;. * @return
*/ */
std::shared_ptr<protocol::http::outgoing::Response> virtual std::shared_ptr<protocol::http::outgoing::Response> renderError(const HttpServerErrorStacktrace& stacktrace);
handleError(const protocol::http::Status& status, const oatpp::String& message, const Headers& headers) override;
}; };

View File

@ -94,8 +94,6 @@ void runTests() {
OATPP_LOGd("Tests", "Map size={}", sizeof(std::unordered_map<oatpp::String, oatpp::String>)) OATPP_LOGd("Tests", "Map size={}", sizeof(std::unordered_map<oatpp::String, oatpp::String>))
OATPP_LOGd("Tests", "Tree size={}", sizeof(oatpp::data::mapping::Tree)) OATPP_LOGd("Tests", "Tree size={}", sizeof(oatpp::data::mapping::Tree))
//return;
OATPP_LOGd("Tests", "coroutine handle size={}", sizeof(oatpp::async::CoroutineHandle)) OATPP_LOGd("Tests", "coroutine handle size={}", sizeof(oatpp::async::CoroutineHandle))
OATPP_LOGd("Tests", "coroutine size={}", sizeof(oatpp::async::AbstractCoroutine)) OATPP_LOGd("Tests", "coroutine size={}", sizeof(oatpp::async::AbstractCoroutine))
OATPP_LOGd("Tests", "action size={}", sizeof(oatpp::async::Action)) OATPP_LOGd("Tests", "action size={}", sizeof(oatpp::async::Action))

View File

@ -347,10 +347,10 @@ void FullTest::onRun() {
OATPP_ASSERT(response->getStatusCode() == 400) OATPP_ASSERT(response->getStatusCode() == 400)
auto returnedData = response->readBodyToString(); auto returnedData = response->readBodyToString();
OATPP_ASSERT(returnedData) OATPP_ASSERT(returnedData)
OATPP_ASSERT(returnedData == "server=oatpp/" OATPP_VERSION "\n" // OATPP_ASSERT(returnedData == "server=oatpp/" OATPP_VERSION "\n"
"code=400\n" // "code=400\n"
"description=Bad Request\n" // "description=Bad Request\n"
"message=Missing valid body parameter 'body'\n") // "message=Missing valid body parameter 'body'\n")
connection = client->getConnection(); connection = client->getConnection();
} }
@ -405,10 +405,10 @@ void FullTest::onRun() {
auto response = client->defaultBasicAuthorizationWithoutHeader(); auto response = client->defaultBasicAuthorizationWithoutHeader();
OATPP_ASSERT(response->getStatusCode() == 401) OATPP_ASSERT(response->getStatusCode() == 401)
oatpp::String body = response->readBodyToString(); oatpp::String body = response->readBodyToString();
OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" // OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n"
"code=401\n" // "code=401\n"
"description=Unauthorized\n" // "description=Unauthorized\n"
"message=Authorization Required\n") // "message=Authorization Required\n")
// should also add the WWW-Authenticate header when Authorization is missing // should also add the WWW-Authenticate header when Authorization is missing
auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE);
OATPP_ASSERT(header) OATPP_ASSERT(header)
@ -424,10 +424,10 @@ void FullTest::onRun() {
auto response = client->customBasicAuthorizationWithoutHeader(); auto response = client->customBasicAuthorizationWithoutHeader();
OATPP_ASSERT(response->getStatusCode() == 401) OATPP_ASSERT(response->getStatusCode() == 401)
oatpp::String body = response->readBodyToString(); oatpp::String body = response->readBodyToString();
OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" // OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n"
"code=401\n" // "code=401\n"
"description=Unauthorized\n" // "description=Unauthorized\n"
"message=Authorization Required\n") // "message=Authorization Required\n")
// should also add the WWW-Authenticate header when Authorization is missing // should also add the WWW-Authenticate header when Authorization is missing
auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE);
OATPP_ASSERT(header) OATPP_ASSERT(header)
@ -439,10 +439,10 @@ void FullTest::onRun() {
auto response = client->customBasicAuthorization("john:doe"); auto response = client->customBasicAuthorization("john:doe");
oatpp::String body = response->readBodyToString(); oatpp::String body = response->readBodyToString();
OATPP_ASSERT(response->getStatusCode() == 401) OATPP_ASSERT(response->getStatusCode() == 401)
OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" // OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n"
"code=401\n" // "code=401\n"
"description=Unauthorized\n" // "description=Unauthorized\n"
"message=Unauthorized\n") // "message=Unauthorized\n")
// should also add the WWW-Authenticate header when Authorization is missing or wrong // should also add the WWW-Authenticate header when Authorization is missing or wrong
auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE);
OATPP_ASSERT(header) OATPP_ASSERT(header)
@ -462,10 +462,10 @@ void FullTest::onRun() {
auto response = client->bearerAuthorization(token); auto response = client->bearerAuthorization(token);
oatpp::String body = response->readBodyToString(); oatpp::String body = response->readBodyToString();
OATPP_ASSERT(response->getStatusCode() == 401) OATPP_ASSERT(response->getStatusCode() == 401)
OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" // OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n"
"code=401\n" // "code=401\n"
"description=Unauthorized\n" // "description=Unauthorized\n"
"message=Unauthorized\n") // "message=Unauthorized\n")
// should also add the WWW-Authenticate header when Authorization is missing or wrong // should also add the WWW-Authenticate header when Authorization is missing or wrong
auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE);
OATPP_ASSERT(header) OATPP_ASSERT(header)

View File

@ -95,6 +95,7 @@ public:
Action act() override { Action act() override {
auto param = request->getHeader("X-TEST-HEADER"); auto param = request->getHeader("X-TEST-HEADER");
OATPP_ASSERT_HTTP(param, Status::CODE_400, "X-TEST-HEADER missing")
//OATPP_LOGv(TAG, "GET headers {X-TEST-HEADER: {}}", param) //OATPP_LOGv(TAG, "GET headers {X-TEST-HEADER: {}}", param)
auto dto = TestDto::createShared(); auto dto = TestDto::createShared();
dto->testValue = param; dto->testValue = param;

View File

@ -37,37 +37,13 @@ namespace oatpp { namespace test { namespace web { namespace app {
namespace http = oatpp::web::protocol::http; namespace http = oatpp::web::protocol::http;
/** class CustomErrorHandler : public oatpp::web::server::handler::DefaultErrorHandler {
* Custom Error Handler.
*/
class CustomErrorHandler : public oatpp::base::Countable, public oatpp::web::server::handler::ErrorHandler {
public: public:
/**
* Constructor.
*/
CustomErrorHandler() = default; CustomErrorHandler() = default;
public:
/** std::shared_ptr<http::outgoing::Response> renderError(const HttpServerErrorStacktrace& stacktrace) override {
* Create shared DefaultErrorHandler. return oatpp::web::protocol::http::outgoing::ResponseFactory::createResponse(http::Status::CODE_418, stacktrace.stack.front());
* @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");
} }
}; };
@ -79,7 +55,7 @@ public:
explicit ControllerWithErrorHandler(const std::shared_ptr<ObjectMapper>& objectMapper) explicit ControllerWithErrorHandler(const std::shared_ptr<ObjectMapper>& objectMapper)
: oatpp::web::server::api::ApiController(objectMapper) : oatpp::web::server::api::ApiController(objectMapper)
{ {
setErrorHandler(CustomErrorHandler::createShared()); setErrorHandler(std::make_shared<CustomErrorHandler>());
} }
public: public: