mirror of
https://github.com/drogonframework/drogon.git
synced 2025-08-29 00:02:44 -04:00
Catch exceptions thrown by handlers (#773)
This commit is contained in:
parent
564fc67649
commit
a19d0427ed
@ -160,6 +160,7 @@ set(DROGON_SOURCES
|
||||
lib/src/DrTemplateBase.cc
|
||||
lib/src/FiltersFunction.cc
|
||||
lib/src/HttpAppFrameworkImpl.cc
|
||||
lib/src/HttpBinder.cc
|
||||
lib/src/HttpClientImpl.cc
|
||||
lib/src/HttpControllersRouter.cc
|
||||
lib/src/HttpFileImpl.cc
|
||||
|
@ -205,6 +205,12 @@ int main()
|
||||
int)>
|
||||
func = std::bind(&A::handle, &tmp, _1, _2, _3, _4, _5, _6);
|
||||
app().registerHandler("/api/v1/handle4/{4:p4}/{3:p3}/{1:p1}", func);
|
||||
app().registerHandler(
|
||||
"/api/v1/this_will_fail",
|
||||
[](const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback) {
|
||||
throw std::runtime_error("this should fail");
|
||||
});
|
||||
|
||||
app().setDocumentRoot("./");
|
||||
app().enableSession(60);
|
||||
|
@ -1303,6 +1303,27 @@ void doTest(const HttpClientPtr &client,
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
req = HttpRequest::newHttpRequest();
|
||||
req->setMethod(drogon::Get);
|
||||
req->setPath("/api/v1/this_will_fail");
|
||||
client->sendRequest(
|
||||
req, [req, isHttps](ReqResult result, const HttpResponsePtr &resp) {
|
||||
if (result == ReqResult::Ok)
|
||||
{
|
||||
if (resp->getStatusCode() != k500InternalServerError)
|
||||
{
|
||||
LOG_DEBUG << resp->getStatusCode();
|
||||
LOG_ERROR << "Error!";
|
||||
exit(1);
|
||||
}
|
||||
outputGood(req, isHttps);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR << "Error!";
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef __cpp_impl_coroutine
|
||||
// Test coroutine requests
|
||||
|
@ -56,6 +56,10 @@ std::string getGitCommit();
|
||||
class HttpControllerBase;
|
||||
class HttpSimpleControllerBase;
|
||||
class WebSocketControllerBase;
|
||||
using ExceptionHandler =
|
||||
std::function<void(const std::exception &,
|
||||
const HttpRequestPtr &,
|
||||
std::function<void(const HttpResponsePtr &)> &&)>;
|
||||
|
||||
class HttpAppFramework : public trantor::NonCopyable
|
||||
{
|
||||
@ -1293,6 +1297,16 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
*/
|
||||
virtual bool reusePort() const = 0;
|
||||
|
||||
/**
|
||||
* @brief handler will be called upon an exception escapes a request handler
|
||||
*/
|
||||
virtual void setExceptionHandler(ExceptionHandler handler) = 0;
|
||||
|
||||
/**
|
||||
* @brief returns the excaption handler
|
||||
*/
|
||||
virtual const ExceptionHandler &getExceptionHandler() const = 0;
|
||||
|
||||
private:
|
||||
virtual void registerHttpController(
|
||||
const std::string &pathPattern,
|
||||
|
@ -88,6 +88,10 @@ T &getControllerObj()
|
||||
return obj;
|
||||
}
|
||||
|
||||
void handleException(const std::exception &,
|
||||
const HttpRequestPtr &,
|
||||
std::function<void(const HttpResponsePtr &)> &&);
|
||||
|
||||
using HttpBinderBasePtr = std::shared_ptr<HttpBinderBase>;
|
||||
template <typename FUNCTION>
|
||||
class HttpBinder : public HttpBinderBase
|
||||
@ -236,12 +240,13 @@ class HttpBinder : public HttpBinderBase
|
||||
pathArguments.pop_front();
|
||||
try
|
||||
{
|
||||
getHandlerArgumentValue(value, std::move(v));
|
||||
if (v.empty() == false)
|
||||
getHandlerArgumentValue(value, std::move(v));
|
||||
}
|
||||
catch (...)
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_ERROR << "Error converting string \"" << v << "\" to the "
|
||||
<< sizeof...(Values) + 1 << "th argument";
|
||||
handleException(e, req, std::move(callback));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -250,10 +255,15 @@ class HttpBinder : public HttpBinderBase
|
||||
{
|
||||
value = req->as<ValueType>();
|
||||
}
|
||||
catch (const std::exception &)
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_ERROR << "Error converting HttpRequest to the "
|
||||
<< sizeof...(Values) + 1 << "th argument";
|
||||
handleException(e, req, std::move(callback));
|
||||
return;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_ERROR << "Exception not derived from std::exception";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +283,19 @@ class HttpBinder : public HttpBinderBase
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
Values &&... values)
|
||||
{
|
||||
callFunction(req, std::move(callback), std::move(values)...);
|
||||
try
|
||||
{
|
||||
callFunction(req, callback, std::move(values)...);
|
||||
}
|
||||
catch (const std::exception &except)
|
||||
{
|
||||
handleException(except, req, std::move(callback));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_ERROR << "Exception not derived from std::exception";
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifdef __cpp_impl_coroutine
|
||||
template <typename... Values,
|
||||
@ -294,16 +316,12 @@ class HttpBinder : public HttpBinderBase
|
||||
if constexpr (std::is_same_v<AsyncTask,
|
||||
typename traits::return_type>)
|
||||
{
|
||||
callFunction(req,
|
||||
std::move(callback),
|
||||
std::move(values)...);
|
||||
callFunction(req, callback, std::move(values)...);
|
||||
}
|
||||
else if constexpr (std::is_same_v<Task<>,
|
||||
typename traits::return_type>)
|
||||
{
|
||||
co_await callFunction(req,
|
||||
std::move(callback),
|
||||
std::move(values)...);
|
||||
co_await callFunction(req, callback, std::move(values)...);
|
||||
}
|
||||
else if constexpr (std::is_same_v<Task<HttpResponsePtr>,
|
||||
typename traits::return_type>)
|
||||
@ -313,14 +331,13 @@ class HttpBinder : public HttpBinderBase
|
||||
callback(std::move(resp));
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
catch (const std::exception &except)
|
||||
{
|
||||
LOG_ERROR << "Uncaught exception in " << req->path()
|
||||
<< " what(): " << e.what();
|
||||
handleException(except, req, std::move(callback));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_ERROR << "Uncaught unknown exception in " << req->path();
|
||||
LOG_ERROR << "Exception not derived from std::exception";
|
||||
}
|
||||
}(req, std::move(callback), std::move(values)...);
|
||||
}
|
||||
|
@ -118,6 +118,17 @@ HttpResponsePtr defaultErrorHandler(HttpStatusCode code)
|
||||
return std::make_shared<HttpResponseImpl>(code, CT_TEXT_HTML);
|
||||
}
|
||||
|
||||
void defaultExceptionHandler(
|
||||
const std::exception &e,
|
||||
const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
LOG_ERROR << "Unhandled exception in " << req->query()
|
||||
<< ", what():" << e.what();
|
||||
const auto &handler = app().getCustomErrorHandler();
|
||||
callback(handler(k500InternalServerError));
|
||||
}
|
||||
|
||||
static void godaemon()
|
||||
{
|
||||
printf("Initializing daemon mode\n");
|
||||
|
@ -29,6 +29,9 @@
|
||||
namespace drogon
|
||||
{
|
||||
HttpResponsePtr defaultErrorHandler(HttpStatusCode code);
|
||||
void defaultExceptionHandler(const std::exception &,
|
||||
const HttpRequestPtr &,
|
||||
std::function<void(const HttpResponsePtr &)> &&);
|
||||
|
||||
struct InitBeforeMainFunction
|
||||
{
|
||||
@ -505,6 +508,16 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
|
||||
return reusePort_;
|
||||
}
|
||||
|
||||
void setExceptionHandler(ExceptionHandler handler) override
|
||||
{
|
||||
exceptionHandler_ = std::move(handler);
|
||||
}
|
||||
|
||||
const ExceptionHandler &getExceptionHandler() const override
|
||||
{
|
||||
return exceptionHandler_;
|
||||
}
|
||||
|
||||
private:
|
||||
void registerHttpController(const std::string &pathPattern,
|
||||
const internal::HttpBinderBasePtr &binder,
|
||||
@ -626,6 +639,7 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
|
||||
postRoutingObservers_;
|
||||
std::vector<std::function<void(const HttpRequestPtr &)>>
|
||||
preHandlingObservers_;
|
||||
ExceptionHandler exceptionHandler_{defaultExceptionHandler};
|
||||
};
|
||||
|
||||
} // namespace drogon
|
||||
|
29
lib/src/HttpBinder.cc
Normal file
29
lib/src/HttpBinder.cc
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
*
|
||||
* HttpBinder.h
|
||||
* Martin Chang
|
||||
*
|
||||
* Copyright 2021, Martin Chang. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drogon/HttpBinder.h>
|
||||
#include <drogon/HttpAppFramework.h>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
void handleException(const std::exception& e,
|
||||
const HttpRequestPtr& req,
|
||||
std::function<void(const HttpResponsePtr&)>&& callback)
|
||||
{
|
||||
app().getExceptionHandler()(e, req, std::move(callback));
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace drogon
|
@ -225,32 +225,46 @@ void HttpSimpleControllersRouter::doControllerHandler(
|
||||
}
|
||||
}
|
||||
|
||||
controller->asyncHandleHttpRequest(
|
||||
req,
|
||||
[this, req, callback = std::move(callback), &ctrlBinderPtr](
|
||||
const HttpResponsePtr &resp) {
|
||||
auto newResp = resp;
|
||||
if (resp->expiredTime() >= 0 &&
|
||||
resp->statusCode() != k404NotFound)
|
||||
{
|
||||
// cache the response;
|
||||
static_cast<HttpResponseImpl *>(resp.get())
|
||||
->makeHeaderString();
|
||||
auto loop = req->getLoop();
|
||||
try
|
||||
{
|
||||
controller->asyncHandleHttpRequest(
|
||||
req,
|
||||
[this, req, callback, &ctrlBinderPtr](
|
||||
const HttpResponsePtr &resp) {
|
||||
auto newResp = resp;
|
||||
if (resp->expiredTime() >= 0 &&
|
||||
resp->statusCode() != k404NotFound)
|
||||
{
|
||||
// cache the response;
|
||||
static_cast<HttpResponseImpl *>(resp.get())
|
||||
->makeHeaderString();
|
||||
auto loop = req->getLoop();
|
||||
|
||||
if (loop->isInLoopThread())
|
||||
{
|
||||
ctrlBinderPtr->responseCache_.setThreadData(resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
loop->queueInLoop([resp, &ctrlBinderPtr]() {
|
||||
if (loop->isInLoopThread())
|
||||
{
|
||||
ctrlBinderPtr->responseCache_.setThreadData(resp);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
loop->queueInLoop([resp, &ctrlBinderPtr]() {
|
||||
ctrlBinderPtr->responseCache_.setThreadData(
|
||||
resp);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
invokeCallback(callback, req, newResp);
|
||||
});
|
||||
invokeCallback(callback, req, newResp);
|
||||
});
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
app().getExceptionHandler()(e, req, std::move(callback));
|
||||
return;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_ERROR << "Exception not derived from std::exception";
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user