123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 |
- /***************************************************************************
- *
- * 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_api_Controller_hpp
- #define oatpp_web_server_api_Controller_hpp
- #include "./Endpoint.hpp"
- #include "oatpp/web/server/handler/AuthorizationHandler.hpp"
- #include "oatpp/web/server/handler/ErrorHandler.hpp"
- #include "oatpp/web/server/handler/AuthorizationHandler.hpp"
- #include "oatpp/web/protocol/http/incoming/Response.hpp"
- #include "oatpp/web/protocol/http/outgoing/Request.hpp"
- #include "oatpp/web/protocol/http/outgoing/ResponseFactory.hpp"
- #include "oatpp/core/utils/ConversionUtils.hpp"
- #include <list>
- #include <unordered_map>
- namespace oatpp { namespace web { namespace server { namespace api {
- /**
- * Class responsible for implementation and management of endpoints.<br>
- * For details see [ApiController](https://oatpp.io/docs/components/api-controller/).
- */
- class ApiController : public oatpp::base::Countable {
- protected:
- typedef ApiController __ControllerType;
- public:
- /**
- * Convenience typedef for &id:oatpp::web::protocol::http::outgoing::ResponseFactory;.
- */
- typedef oatpp::web::protocol::http::outgoing::ResponseFactory ResponseFactory;
- /**
- * Convenience typedef for &id:oatpp::web::protocol::http::incoming::Request;.
- */
- typedef oatpp::web::protocol::http::incoming::Request IncomingRequest;
- /**
- * Convenience typedef for &id:oatpp::web::protocol::http::outgoing::Request;.
- */
- typedef oatpp::web::protocol::http::outgoing::Request OutgoingRequest;
- /**
- * Convenience typedef for &id:oatpp::web::protocol::http::incoming::Response;.
- */
- typedef oatpp::web::protocol::http::incoming::Response IncomingResponse;
- /**
- * Convenience typedef for &id:oatpp::web::protocol::http::outgoing::Response;.
- */
- typedef oatpp::web::protocol::http::outgoing::Response OutgoingResponse;
- /**
- * Convenience typedef for &id:oatpp::web::protocol::http::Status;.
- */
- typedef oatpp::web::protocol::http::Status Status;
- /**
- * Convenience typedef for &id:oatpp::web::protocol::http::Header;.
- */
- typedef oatpp::web::protocol::http::Header Header;
- /**
- * Convenience typedef for &id:oatpp::web::protocol::http::QueryParams;.
- */
- typedef oatpp::web::protocol::http::QueryParams QueryParams;
- /**
- * Convenience typedef for &id:oatpp::web::server::HttpRequestHandler;.
- */
- typedef oatpp::web::server::HttpRequestHandler RequestHandler;
- /**
- * Convenience typedef for &id:oatpp::web::server::handler::AuthorizationHandler;.
- */
- typedef oatpp::web::server::handler::AuthorizationHandler AuthorizationHandler;
-
- public:
- /**
- * Convenience typedef for &id:oatpp::data::mapping::ObjectMapper;.
- */
- typedef oatpp::data::mapping::ObjectMapper ObjectMapper;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::String;.
- */
- typedef oatpp::String String;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::Int8;.
- */
- typedef oatpp::Int8 Int8;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::UInt8;.
- */
- typedef oatpp::UInt8 UInt8;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::Int16;.
- */
- typedef oatpp::Int16 Int16;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::UInt16;.
- */
- typedef oatpp::UInt16 UInt16;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::Int32;.
- */
- typedef oatpp::Int32 Int32;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::UInt32;.
- */
- typedef oatpp::UInt32 UInt32;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::Int64;.
- */
- typedef oatpp::Int64 Int64;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::UInt64;.
- */
- typedef oatpp::UInt64 UInt64;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::Float32;.
- */
- typedef oatpp::Float32 Float32;
- /**
- * Convenience typedef for &id:atpp::data::mapping::type::Float64;.
- */
- typedef oatpp::Float64 Float64;
- /**
- * Convenience typedef for &id:oatpp::data::mapping::type::Boolean;.
- */
- typedef oatpp::Boolean Boolean;
- /*
- * Convenience typedef for std::function<std::shared_ptr<Endpoint::Info>()>.
- */
- typedef std::function<std::shared_ptr<Endpoint::Info>()> EndpointInfoBuilder;
- template <class T>
- using Object = oatpp::Object<T>;
- template <class T>
- using List = oatpp::List<T>;
- template <class Value>
- using Fields = oatpp::Fields<Value>;
- template <class T>
- using Enum = oatpp::data::mapping::type::Enum<T>;
- protected:
-
- /*
- * Endpoint Coroutine base class
- */
- template<class CoroutineT, class ControllerT>
- class HandlerCoroutine : public oatpp::async::CoroutineWithResult<CoroutineT, const std::shared_ptr<OutgoingResponse>&> {
- public:
-
- HandlerCoroutine(ControllerT* pController, const std::shared_ptr<IncomingRequest>& pRequest)
- : controller(pController)
- , request(pRequest)
- {}
-
- ControllerT* const controller;
- std::shared_ptr<IncomingRequest> request;
-
- };
-
- /*
- * Handler which subscribes to specific URL in Router and delegates calls endpoints
- */
- template<class T>
- class Handler : public RequestHandler {
- public:
- typedef std::shared_ptr<OutgoingResponse> (T::*Method)(const std::shared_ptr<IncomingRequest>&);
- typedef oatpp::async::CoroutineStarterForResult<const std::shared_ptr<OutgoingResponse>&>
- (T::*MethodAsync)(const std::shared_ptr<IncomingRequest>&);
- private:
- class ErrorHandlingCoroutine : public oatpp::async::CoroutineWithResult<ErrorHandlingCoroutine, const std::shared_ptr<OutgoingResponse>&> {
- private:
- Handler* m_handler;
- std::shared_ptr<IncomingRequest> m_request;
- public:
- ErrorHandlingCoroutine(Handler* handler, const std::shared_ptr<IncomingRequest>& request)
- : m_handler(handler)
- , m_request(request)
- {}
- async::Action act() override {
- return (m_handler->m_controller->*m_handler->m_methodAsync)(m_request)
- .callbackTo(&ErrorHandlingCoroutine::onResponse);
- }
- async::Action onResponse(const std::shared_ptr<OutgoingResponse>& response) {
- return this->_return(response);
- }
- async::Action handleError(async::Error* error) override {
- auto eptr = std::make_exception_ptr(*error);
- auto response = m_handler->m_controller->m_errorHandler->handleError(eptr);
- return this->_return(response);
- }
- };
- private:
- T* m_controller;
- Method m_method;
- MethodAsync m_methodAsync;
- public:
- Handler(T* controller, Method method, MethodAsync methodAsync)
- : m_controller(controller)
- , m_method(method)
- , m_methodAsync(methodAsync)
- {}
- public:
-
- static std::shared_ptr<Handler> createShared(T* controller, Method method, MethodAsync methodAsync){
- return std::make_shared<Handler>(controller, method, methodAsync);
- }
-
- std::shared_ptr<OutgoingResponse> handle(const std::shared_ptr<IncomingRequest>& request) override {
- if(m_method == nullptr) {
- if(m_methodAsync == nullptr) {
- throw protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Handler method is nullptr.");;
- }
- throw protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Non-async call to async endpoint.");;
- }
- try {
- return (m_controller->*m_method)(request);
- } catch (...) {
- auto response = m_controller->handleError(std::current_exception());
- if(response != nullptr) {
- return response;
- }
- throw;
- }
-
- }
-
- oatpp::async::CoroutineStarterForResult<const std::shared_ptr<OutgoingResponse>&>
- handleAsync(const std::shared_ptr<protocol::http::incoming::Request>& request) override {
- if(m_methodAsync == nullptr) {
- if(m_method == nullptr) {
- throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Handler method is nullptr.");
- }
- throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Async call to non-async endpoint.");
- }
- if(m_controller->m_errorHandler) {
- return ErrorHandlingCoroutine::startForResult(this, request);
- }
- return (m_controller->*m_methodAsync)(request);
- }
- Method setMethod(Method method) {
- auto prev = m_method;
- m_method = method;
- return prev;
- }
- Method getMethod() {
- return m_method;
- }
- MethodAsync setMethodAsync(MethodAsync methodAsync) {
- auto prev = m_methodAsync;
- m_methodAsync = methodAsync;
- return prev;
- }
- MethodAsync getMethodAsync() {
- return m_methodAsync;
- }
-
- };
- protected:
- /*
- * Set endpoint info by endpoint name. (Endpoint name is the 'NAME' parameter of the ENDPOINT macro)
- * Info should be set before call to addEndpointsToRouter();
- */
- void setEndpointInfo(const std::string& endpointName, const std::shared_ptr<Endpoint::Info>& info);
- /*
- * Get endpoint info by endpoint name. (Endpoint name is the 'NAME' parameter of the ENDPOINT macro)
- */
- std::shared_ptr<Endpoint::Info> getEndpointInfo(const std::string& endpointName);
- /*
- * Set endpoint Request handler.
- * @param endpointName
- * @param handler
- */
- void setEndpointHandler(const std::string& endpointName, const std::shared_ptr<RequestHandler>& handler);
- /*
- * Get endpoint Request handler.
- * @param endpointName
- * @return
- */
- std::shared_ptr<RequestHandler> getEndpointHandler(const std::string& endpointName);
-
- protected:
- Endpoints m_endpoints;
- std::shared_ptr<handler::ErrorHandler> m_errorHandler;
- std::shared_ptr<handler::AuthorizationHandler> m_defaultAuthorizationHandler;
- std::shared_ptr<oatpp::data::mapping::ObjectMapper> m_defaultObjectMapper;
- std::unordered_map<std::string, std::shared_ptr<Endpoint::Info>> m_endpointInfo;
- std::unordered_map<std::string, std::shared_ptr<RequestHandler>> m_endpointHandlers;
- const oatpp::String m_routerPrefix;
- public:
- ApiController(const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& defaultObjectMapper, const oatpp::String &routerPrefix = nullptr)
- : m_defaultObjectMapper(defaultObjectMapper)
- , m_routerPrefix(routerPrefix)
- {}
- public:
-
- template<class T>
- static std::shared_ptr<Endpoint> createEndpoint(Endpoints& endpoints,
- const std::shared_ptr<Handler<T>>& handler,
- const EndpointInfoBuilder& infoBuilder)
- {
- auto endpoint = Endpoint::createShared(handler, infoBuilder);
- endpoints.append(endpoint);
- return endpoint;
- }
-
- /**
- * Get list of Endpoints created via ENDPOINT macro
- */
- const Endpoints& getEndpoints();
- /**
- * Set error handler to handle errors that occur during the endpoint's execution
- */
- void setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler);
- /**
- * 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 std::exception_ptr& exceptionPtr) const;
- /**
- * [under discussion]
- * Set authorization handler to handle calls to handleAuthorization.
- * Must be called before controller is added to a router or swagger-doc if an endpoint uses the AUTHORIZATION macro
- */
- void setDefaultAuthorizationHandler(const std::shared_ptr<handler::AuthorizationHandler>& authorizationHandler);
- /**
- * Get authorization handler.
- * @return
- */
- std::shared_ptr<handler::AuthorizationHandler> getDefaultAuthorizationHandler();
- /**
- * [under discussion]
- * Do not use it directly. This method is under discussion.
- * Currently returns AuthorizationObject created by AuthorizationHandler or return DefaultAuthorizationObject by DefaultAuthorizationHandler if AuthorizationHandler is null
- */
- std::shared_ptr<handler::AuthorizationObject> handleDefaultAuthorization(const String &authHeader) const;
-
- const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& getDefaultObjectMapper() const;
-
- // Helper methods
-
- std::shared_ptr<OutgoingResponse> createResponse(const Status& status,
- const oatpp::String& str) const;
-
- std::shared_ptr<OutgoingResponse> createResponse(const Status& status) const;
- std::shared_ptr<OutgoingResponse> createDtoResponse(const Status& status,
- const oatpp::Void& dto,
- const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& objectMapper) const;
-
- std::shared_ptr<OutgoingResponse> createDtoResponse(const Status& status,
- const oatpp::Void& dto) const;
- public:
- template<typename T>
- struct TypeInterpretation {
- static T fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) text;
- success = false;
- OATPP_LOGE("[oatpp::web::server::api::ApiController::TypeInterpretation::fromString()]",
- "Error. No conversion from '%s' to '%s' is defined.", "oatpp::String", typeName->c_str());
- throw std::runtime_error("[oatpp::web::server::api::ApiController::TypeInterpretation::fromString()]: Error. "
- "No conversion from 'oatpp::String' to '" + *typeName + "' is defined. "
- "Please define type conversion.");
- }
- };
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::String> {
- static oatpp::String fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- success = true;
- return text;
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::Int8> {
- static oatpp::Int8 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- //TODO: check the range and perhaps throw an exception if the variable doesn't fit
- return static_cast<Int8::UnderlyingType>(utils::conversion::strToInt32(text, success));
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::UInt8> {
- static oatpp::UInt8 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- //TODO: check the range and perhaps throw an exception if the variable doesn't fit
- return static_cast<UInt8::UnderlyingType>(utils::conversion::strToUInt32(text, success));
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::Int16> {
- static oatpp::Int16 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- //TODO: check the range and perhaps throw an exception if the variable doesn't fit
- return static_cast<Int16::UnderlyingType>(utils::conversion::strToInt32(text, success));
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::UInt16> {
- static oatpp::UInt16 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- //TODO: check the range and perhaps throw an exception if the variable doesn't fit
- return static_cast<UInt16::UnderlyingType>(utils::conversion::strToUInt32(text, success));
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::Int32> {
- static oatpp::Int32 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- return utils::conversion::strToInt32(text, success);
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::UInt32> {
- static oatpp::UInt32 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- return utils::conversion::strToUInt32(text, success);
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::Int64> {
- static oatpp::Int64 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- return utils::conversion::strToInt64(text, success);
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::UInt64> {
- static oatpp::UInt64 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- return utils::conversion::strToUInt64(text, success);
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::Float32> {
- static oatpp::Float32 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- return utils::conversion::strToFloat32(text, success);
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::Float64> {
- static oatpp::Float64 fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- return utils::conversion::strToFloat64(text, success);
- }
- };
- template<>
- struct ApiController::TypeInterpretation <oatpp::Boolean> {
- static oatpp::Boolean fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- (void) typeName;
- return utils::conversion::strToBool(text, success);
- }
- };
- template<class T, class I>
- struct ApiController::TypeInterpretation <data::mapping::type::EnumObjectWrapper<T, I>> {
- typedef data::mapping::type::EnumObjectWrapper<T, I> EnumOW;
- typedef typename I::UnderlyingTypeObjectWrapper UTOW;
- static EnumOW fromString(const oatpp::String& typeName, const oatpp::String& text, bool& success) {
- const auto& parsedValue = ApiController::TypeInterpretation<UTOW>::fromString(typeName, text, success);
- if(success) {
- data::mapping::type::EnumInterpreterError error = data::mapping::type::EnumInterpreterError::OK;
- const auto& result = I::fromInterpretation(parsedValue, error);
- if(error == data::mapping::type::EnumInterpreterError::OK) {
- return result.template cast<EnumOW>();
- }
- success = false;
- }
- return nullptr;
- }
- };
- }}}}
- #endif /* oatpp_web_server_api_Controller_hpp */
|