Continue to improve the api controller, not yet completed

This commit is contained in:
an-tao 2018-06-04 19:00:35 +08:00
parent 5cc1ab00e5
commit a8bc891209
5 changed files with 230 additions and 46 deletions

View File

@ -1,15 +1,33 @@
#include <iostream>
#include <drogon/HttpAppFramework.h>
#include <trantor/utils/Logger.h>
#include <drogon/HttpApiBinder.h>
#include <vector>
#include <string>
using namespace drogon;
class A
{
public:
void handle(const HttpRequest& req,std::function<void (HttpResponse &)>callback,int aa,const std::string &a,const std::string &b,int haha)
{
LOG_DEBUG<<"int aa="<<aa;
LOG_DEBUG<<"string a="<<a;
LOG_DEBUG<<"string b="<<b;
LOG_DEBUG<<"int haha="<<haha;
}
};
int main()
{
std::cout<<banner<<std::endl;
auto bindPtr=std::make_shared<drogon::HttpApiBinder<decltype(&A::handle)>>(&A::handle);
//drogon::HttpApiBinder<A, decltype(&A::handle)> binder(&A::handle);
// binder.test();
drogon::HttpAppFramework::instance().addListener("0.0.0.0",12345);
drogon::HttpAppFramework::instance().addListener("0.0.0.0",8080);
trantor::Logger::setLogLevel(trantor::Logger::INFO);
trantor::Logger::setLogLevel(trantor::Logger::TRACE);
drogon::HttpAppFramework::instance().registerHttpApiController("/api/v1","",bindPtr);
drogon::HttpAppFramework::instance().run();
}

102
lib/inc/drogon/HttpApiBinder.h Executable file
View File

@ -0,0 +1,102 @@
/**
*
* @file
* @author An Tao
* @section LICENSE
*
* Copyright 2018, An Tao. All rights reserved.
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
*
*/
#pragma once
#include <drogon/HttpRequest.h>
#include <drogon/HttpResponse.h>
#include <drogon/utils/FunctionTraits.h>
#include <list>
#include <string>
#include <sstream>
#include <memory>
namespace drogon{
class HttpApiBinderBase
{
public:
virtual void handleHttpApiRequest(std::list<std::string> &pathParameter,
const HttpRequest& req,std::function<void (HttpResponse &)>callback)
=0;
virtual ~HttpApiBinderBase(){}
};
typedef std::shared_ptr<HttpApiBinderBase> HttpApiBinderBasePtr;
template <typename FUNCTION>
class HttpApiBinder:public HttpApiBinderBase
{
public:
virtual void handleHttpApiRequest(std::list<std::string> &pathParameter,
const HttpRequest& req,std::function<void (HttpResponse &)>callback) override
{
run(pathParameter,req,callback);
}
HttpApiBinder(FUNCTION func):
_func(func)
{
static_assert(traits::isHTTPApiFunction,"Your API handler function interface is wrong!");
}
void test(){
std::cout<<"argument_count="<<argument_count<<" "<<traits::isHTTPApiFunction<<std::endl;
}
private:
FUNCTION _func;
typedef utility::FunctionTraits<FUNCTION> traits;
template <
std::size_t Index
>
using nth_argument_type = typename traits::template argument<Index>;
static const size_t argument_count = traits::arity;
template<
typename... Values,
std::size_t Boundary = argument_count
>
typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run(
std::list<std::string> &pathParameter,
const HttpRequest& req,std::function<void (HttpResponse &)>callback,
Values&&... values
) {
typedef typename std::remove_cv<typename std::remove_reference<nth_argument_type<sizeof...(Values)>>::type>::type ValueType;
ValueType value;
if(!pathParameter.empty())
{
std::string v=std::move(pathParameter.front());
pathParameter.pop_front();
std::stringstream ss(std::move(v));
ss>>value;
}
run(pathParameter,req,callback, std::forward<Values>(values)..., std::move(value));
}
template<
typename... Values,
std::size_t Boundary = argument_count
>
typename std::enable_if<(sizeof...(Values) == Boundary), void>::type run(
std::list<std::string> &pathParameter,
const HttpRequest& req,std::function<void (HttpResponse &)>callback,
Values&&... values
)
{
//new object per time or create a object in constructor???
std::unique_ptr<typename traits::class_type> ptr(new typename traits::class_type);
(ptr.get()->*_func)(req,callback,std::move(values)...);
}
};
}

View File

@ -14,6 +14,7 @@
#pragma once
#include <drogon/HttpApiBinder.h>
#include <trantor/utils/NonCopyable.h>
#include <drogon/DrObject.h>
#include <drogon/HttpRequest.h>
@ -28,7 +29,6 @@
#include <string>
#include <functional>
#include <vector>
namespace drogon
{
//the drogon banner
@ -53,8 +53,13 @@ namespace drogon
virtual void addListener(const std::string &ip,uint16_t port)=0;
virtual void run()=0;
virtual ~HttpAppFramework();
virtual void registerHttpSimpleController(const std::string &pathName,const std::string &crtlName,const std::vector<std::string> &filters=
std::vector<std::string>())=0;
virtual void registerHttpSimpleController(const std::string &pathName,
const std::string &crtlName,
const std::vector<std::string> &filters=std::vector<std::string>())=0;
virtual void registerHttpApiController(const std::string &pathName,
const std::string &parameterPattern,
const HttpApiBinderBasePtr &binder,
const std::vector<std::string> &filters=std::vector<std::string>())=0;
virtual void enableSession(const size_t timeout=0)=0;
virtual void disableSession()=0;
};

View File

@ -2,52 +2,72 @@
#include <tuple>
#include<type_traits>
namespace drogon{
class HttpRequest;
class HttpResponse;
namespace utility {
namespace utility {
template<typename> struct FunctionTraits;
template<typename> struct FunctionTraits;
template <typename Function>
struct FunctionTraits : public FunctionTraits<
decltype(&std::remove_reference<Function>::type::operator())
> { };
template <typename Function>
struct FunctionTraits : public FunctionTraits<
decltype(&std::remove_reference<Function>::type::operator())
> { };
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct FunctionTraits<
ReturnType(ClassType::*)(Arguments...) const
> : FunctionTraits<ReturnType(*)(Arguments...)> { };
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct FunctionTraits<
ReturnType(ClassType::*)(Arguments...) const
> : FunctionTraits<ReturnType(*)(Arguments...)> { };
/* support the non-const operator ()
* this will work with user defined functors */
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct FunctionTraits<
ReturnType(ClassType::*)(Arguments...)
> : FunctionTraits<ReturnType(*)(Arguments...)> {
static const bool isClassFunction=true;
};
/* support the non-const operator ()
* this will work with user defined functors */
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct FunctionTraits<
ReturnType(ClassType::*)(Arguments...)
> : FunctionTraits<ReturnType(*)(Arguments...)> { };
template <
typename ReturnType,
typename... Arguments
>
struct FunctionTraits<
ReturnType(*)(Arguments...)
> {
typedef ReturnType result_type;
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct FunctionTraits<
ReturnType(ClassType::*)(const HttpRequest& req,std::function<void (HttpResponse &)>callback,Arguments...)
> : FunctionTraits<ReturnType(*)(Arguments...)> {
static const bool isHTTPApiFunction=true;
typedef ClassType class_type;
};
template <std::size_t Index>
using argument = typename std::tuple_element<
Index,
std::tuple<Arguments...>
>::type;
template <
typename ReturnType,
typename... Arguments
>
struct FunctionTraits<
ReturnType(*)(Arguments...)
> {
typedef ReturnType result_type;
static const std::size_t arity = sizeof...(Arguments);
};
template <std::size_t Index>
using argument = typename std::tuple_element<
Index,
std::tuple<Arguments...>
>::type;
static const std::size_t arity = sizeof...(Arguments);
static const bool isHTTPApiFunction=false;
};
}
}

View File

@ -40,6 +40,10 @@ namespace drogon
virtual void run() override ;
virtual void registerHttpSimpleController(const std::string &pathName,const std::string &crtlName,const std::vector<std::string> &filters=
std::vector<std::string>())override ;
virtual void registerHttpApiController(const std::string &pathName,
const std::string &parameterPattern,
const HttpApiBinderBasePtr &binder,
const std::vector<std::string> &filters=std::vector<std::string>()) override ;
virtual void enableSession(const size_t timeout) override { _useSession=true;_sessionTimeout=timeout;}
virtual void disableSession() override { _useSession=false;}
~HttpAppFrameworkImpl(){}
@ -64,6 +68,15 @@ namespace drogon
std::unordered_map<std::string,ControllerAndFiltersName>_simpCtrlMap;
std::mutex _simpCtrlMutex;
struct ApiBinder
{
std::string parameterPattern;
HttpApiBinderBasePtr binderPtr;
std::vector<std::string> filtersName;
};
std::unordered_map<std::string,ApiBinder>_apiCtrlMap;
std::mutex _apiCtrlMutex;
bool _enableLastModify=true;
std::set<std::string> _fileTypeSet={"html","jpg"};
std::string _rootPath;
@ -92,6 +105,21 @@ void HttpAppFrameworkImpl::registerHttpSimpleController(const std::string &pathN
_simpCtrlMap[path].controllerName=crtlName;
_simpCtrlMap[path].filtersName=filters;
}
void HttpAppFrameworkImpl::registerHttpApiController(const std::string &pathName,
const std::string &parameterPattern,
const HttpApiBinderBasePtr &binder,
const std::vector<std::string> &filters)
{
assert(!pathName.empty());
assert(binder);
std::string path(pathName);
std::transform(pathName.begin(),pathName.end(),path.begin(),tolower);
std::lock_guard<std::mutex> guard(_apiCtrlMutex);
_apiCtrlMap[path].parameterPattern=parameterPattern;
_apiCtrlMap[path].binderPtr=binder;
_apiCtrlMap[path].filtersName=filters;
}
void HttpAppFrameworkImpl::addListener(const std::string &ip, uint16_t port)
{
_listeners.push_back(std::make_pair(ip,port));
@ -217,6 +245,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequest& req,std::function<v
/*find controller*/
std::string pathLower(req.path());
std::transform(pathLower.begin(),pathLower.end(),pathLower.begin(),tolower);
//fix me!need mutex;
if(_simpCtrlMap.find(pathLower)!=_simpCtrlMap.end())
{
auto filters=_simpCtrlMap[pathLower].filtersName;
@ -259,7 +288,17 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequest& req,std::function<v
LOG_ERROR << "can't find controller " << ctrlName;
}
}
//find api controller
//fix me!need mutex;
if(_apiCtrlMap.find(pathLower)!=_apiCtrlMap.end())
{
//for test;
LOG_DEBUG<<"got api controller";
std::list<std::string> para={"1","2","3","4.5","5"};
auto binder=_apiCtrlMap[pathLower];
binder.binderPtr->handleHttpApiRequest(para,req,callback);
return;
}
auto res=drogon::HttpResponse::notFoundResponse();
if(needSetJsessionid)