欢迎光临
我们一直在努力

c++web服务器框架Oat++的API应用

引言

这是一个轻量且高效的c++web服务框架,本篇文章会讲述如何使用其API,并且是一部分的原理和其中的架构。

代码讲解

首先我们先构建一个简单的架构,这个简单的架构由四个部分组成

router:路由器,这个负责接受我们的路径然后进行对应的匹配,匹配成功之后交给业务处理函数。

connectionHandler:这个是负责解析客户的请求的,然后才交给router来处理,里面的工作一个是要解析请求数据,然后调用对应的router。

connectionProvider:这个是负责监听客户的连接的,通过socket的accept(),当有对象来的时候就把对象封装成oatpp::network::Connection,然后交给connectionHandler

#include "oatpp/web/server/HttpConnectionHandler.hpp"

#include "oatpp/network/Server.hpp"
#include "oatpp/network/tcp/server/ConnectionProvider.hpp"

void run() {

/* Create Router for HTTP requests routing */
auto router = oatpp::web::server::HttpRouter::createShared();

/* Create HTTP connection handler with router */
auto connectionHandler = oatpp::web::server::HttpConnectionHandler::createShared(router);

/* Create TCP connection provider */
auto connectionProvider = oatpp::network::tcp::server::ConnectionProvider::createShared({"localhost", 8000, oatpp::network::Address::IP_4});

/* Create server which takes provided TCP connections and passes them to HTTP connection handler */
oatpp::network::Server server(connectionProvider, connectionHandler);

/* Print info about server port */
OATPP_LOGI("MyApp", "Server running on port %s", connectionProvider->getProperty("port").getData());

/* Run server */
server.run();
}

int main() {

/* Init oatpp Environment */
oatpp::base::Environment::init();

/* Run App */
run();

/* Destroy oatpp Environment */
oatpp::base::Environment::destroy();

return 0;

}

router不是要匹配对应的请求吗,我们这里假设请求方式就是GET,路径是/hello,然后传入处理的函数的指针,这个处理函数里面,我们返回一个状态码和我们要输出的东西

class Handler : public oatpp::web::server::HttpRequestHandler {
public:

/**
* Handle incoming request and return outgoing response.
*/
std::shared_ptr<OutgoingResponse> handle(const std::shared_ptr<IncomingRequest>& request) override {
return ResponseFactory::createResponse(Status::CODE_200, "Hello World!");
}

};

auto router = oatpp::web::server::HttpRouter::createShared();

/* Route GET – "/hello" requests to Handler */
router->route("GET", "/hello", std::make_shared<Handler>());

然后这一个部分是序列化和反序列化的一个过程,我们这里涉及到了一个预编译的操作,就是在这个领域内,我们会提前采取预编译的操作。

然后这里我们使用DTO的形式传递数据,我们上面打印的hello world是纯文本的形式,那如果我们需要传输结构化的数据(例如JSON),我们就需要DTO

#include OATPP_CODEGEN_BEGIN(DTO)

/**
* Message Data-Transfer-Object
*/
class MessageDto : public oatpp::DTO {

DTO_INIT(MessageDto, DTO /* Extends */)

DTO_FIELD(Int32, statusCode); // Status code field
DTO_FIELD(String, message); // Message field

};

/* End DTO code-generation */
#include OATPP_CODEGEN_END(DTO)

如何使用,如下:

ENDPOINT("GET", "/hello", getHello) {
// 创建 DTO 对象
auto dto = MessageDto::createShared();
dto->statusCode = 200;
dto->message = "Hello World!";

// 用 createDtoResponse 自动序列化成 JSON
return createDtoResponse(Status::CODE_200, dto);
}

不过在官方给的文档里面,我们有一些改进的地方:

首先就是加入了objecterMapper,这个其实就是一个帮忙处理序列化和反序列化的东西。

class Handler : public oatpp::web::server::HttpRequestHandler {
private:
std::shared_ptr<oatpp::data::mapping::ObjectMapper> m_objectMapper;
public:

/**
* Constructor with object mapper.
* @param objectMapper – object mapper used to serialize objects.
*/
Handler(const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& objectMapper)
: m_objectMapper(objectMapper)
{}

/**
* Handle incoming request and return outgoing response.
*/
std::shared_ptr<OutgoingResponse> handle(const std::shared_ptr<IncomingRequest>& request) override {
auto message = MessageDto::createShared();
message->statusCode = 1024;
message->message = "Hello DTO!";
return ResponseFactory::createResponse(Status::CODE_200, message, m_objectMapper);
}

};
auto objectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared();
router->route("GET", "/hello", std::make_shared<Handler>(objectMapper /* json object mapper */ ));

以上就是我们的一个基本的架构,接下来就是一些具体的实现了

 OATPP_CREATE_COMPONENT是用来初始化环境的,并在需要的地方注入OATPP_COMPONENT。

前者是创建一个实体组件放到全局容器里面,后者是从全局容器里面取出对应的组件

大家可以看到第一个部分传递的参数有主机的名称,有端口号,有通信协议,说明这个就是与客户端监听的地方。然后所有的部分里面只有ConnectionHandler不仅仅返回了实体,还创建了router,这个是因为每个ConnectionHandler都会对应一个router,解析完数据之后就会立刻调用router,根据路径匹配对应的业务函数,所以这里我们创建的时候要传递router,这样子每个ConnectionHandler都会有对应路由器的指针

class AppComponent {
public:

/**
* Create ConnectionProvider component which listens on the port
*/

OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ServerConnectionProvider>, serverConnectionProvider)([] {
return oatpp::network::tcp::server::ConnectionProvider::createShared({"localhost", 8000, oatpp::network::Address::IP_4});
}());

/**
* Create Router component
*/

OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, httpRouter)([] {
return oatpp::web::server::HttpRouter::createShared();
}());

/**
* Create ConnectionHandler component which uses Router component to route requests
*/

OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ConnectionHandler>, serverConnectionHandler)([] {
OATPP_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, router); // get Router component
return oatpp::web::server::HttpConnectionHandler::createShared(router);
}());

/**
* Create ObjectMapper component to serialize/deserialize DTOs in Contoller's API
*/

OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>, apiObjectMapper)([] {
return oatpp::parser::json::mapping::ObjectMapper::createShared();
}());

};

/* Register Components in scope of run() method */
AppComponent components;

/* Get router component */
OATPP_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, router);

/* Route GET – "/hello" requests to Handler */
router->route("GET", "/hello", std::make_shared<Handler>());

/* Get connection handler component */
OATPP_COMPONENT(std::shared_ptr<oatpp::network::ConnectionHandler>, connectionHandler);

/* Get connection provider component */
OATPP_COMPONENT(std::shared_ptr<oatpp::network::ServerConnectionProvider>, connectionProvider);

/* Create server which takes provided TCP connections and passes them to HTTP connection handler */
oatpp::network::Server server(connectionProvider, connectionHandler);

接下来我们就要用API创建一个请求处理层(controller),这里我们会直接调用接口来实现:

首先这个是一个基本的框架,我们的所有实现都是在这个上面。首先是一个构造函数,这个构造函数里面初始化了一个 objectMapper,这个的目的就是后面的createresponse里面反序列化和序列化的操作

#include "oatpp/web/server/api/ApiController.hpp"
#include "oatpp/core/macro/codegen.hpp"

#include OATPP_CODEGEN_BEGIN(ApiController) ///< Begin ApiController codegen section

class MyController : public oatpp::web::server::api::ApiController {
public:

MyController(OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper) /* Inject object mapper */)
: oatpp::web::server::api::ApiController(objectMapper)
{}

ENDPOINT("GET", "/", root) {
return createResponse(Status::CODE_200, "Hello World!");
}

// TODO – more endpoints here

};

#include OATPP_CODEGEN_END(ApiController) ///< End ApiController codegen section

我们这里写一个认证的接口,就是判断用户是否符注册需求,我们这里的端点结构选择这个:

需要额外说的就是最后一个参数,就是我们的业务函数QUERIES(QueryParams, queryParams)第一个参数是传递的参数类型,第二个就是参数本身。

ENDPOINT("GET", "/users", getUsers,
QUERIES(QueryParams, queryParams))
{
for(auto& param : queryParams.getAll()) {
OATPP_LOGD("param", "%s=%s", param.first.getData(), param.second.getData());
}
return createResponse(Status::CODE_200, "OK");
}

当然还有另外一种表达方式就是

ENDPOINT("GET", "/users", getUsers,
QUERY(Int32, age, "user-age"))
{
OATPP_LOGD("Test", "age=%d", age->getValue());
return createResponse(Status::CODE_200, "OK");
}

所以我们的大致模板就是:

class MyController : public oatpp::web::server::api::ApiController {
public:

MyController(OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper) /* Inject object mapper */)
: oatpp::web::server::api::ApiController(objectMapper)
{
setDefaultAuthorizationHandler(std::make_shared<MyBasicAuthorizationHandler>());
}

ENDPOINT("GET", "/my/secret/resource", getResource,
AUTHORIZATION(std::shared_ptr<MyAuthorizationObject>, authObject))
{
OATPP_ASSERT_HTTP(authObject->userId == "uid-admin", Status::CODE_401, "Unauthorized");
return createResponse(Status::CODE_200, "OK");
}

};

然后我们需要生成API文档页面,所以需要调用

ENDPOINT_INFO(createUser) {
info->summary = "Create new User";
info->addConsumes<Object<UserDto>>("application/json");
info->addResponse<Object<UserDto>>(Status::CODE_200, "application/json");
}
ENDPOINT("POST", "demo/api/users", createUser,
BODY_DTO(Object<UserDto>, userDto)) {
return createDtoResponse(Status::CODE_200, m_database->createUser(userDto));
}

最后的代码差不多就是这个样子:

#include OATPP_CODEGEN_BEGIN(ApiController) ///< Begin ApiController codegen section

class controller : public oatpp::web::server::api::ApiController {
public:
// 定义访问的入口
controller(OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper) /* Inject object mapper */)
: oatpp::web::server::api::ApiController(objectMapper)
{
setDefaultAuthorizationHandler(std::make_shared<BasicAuthorizationHandler>("my-realm"));
}

static std::shared_ptr<controller> createShared(OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) {
return std::make_shared<controller>(objectMapper);
}

ENDPOINT_INFO(putResource) {
info->summary = "Put Resource";
info->addConsumes<ObjectMapper::Info>("application/json");
info->addResponse<String>(Status::CODE_200, "text/plain");
}

// 定义查询接口描述
ENDPOINT("PUT", "/test", putResource, AUTHORIZATION(std::shared_ptr<DefaultBasicAuthorizationObject>, authObject)) {
OATPP_ASSERT_HTTP(authObject->userId == "uid-admin", Status::CODE_401, "Unauthorized");
return createResponse(Status::CODE_200, "OK");
}
};

#include OATPP_CODEGEN_END(ApiController) ///< End ApiController codegen section

赞(0)
未经允许不得转载:171主机测评 » c++web服务器框架Oat++的API应用
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址