当前位置: 首页 > news >正文

Ota++框架学习

一:框架结构

这是一幅展现 Web 应用程序架构的示意图,以下是对图中各部分的详细解释:
外部交互部分
Request(请求):位于架构图的左上角,用黄色虚线框表示 。代表来自客户端(如浏览器、移动应用等)的请求,是整个流程的起始点,触发服务器端的处理逻辑。
CROS(跨域资源共享):以红色矩形呈现。当浏览器发起跨域请求时,CROS 机制会处理跨域相关的配置和验证,确保不同源之间的资源请求符合安全策略。它是保障浏览器跨域请求正常进行的关键组件。
Response(响应):在左下角,以紫色虚线框展示。是服务器处理完请求后,返回给客户端的数据或状态信息,携带处理结果。
服务器内部组件
Controller(控制器):绿色矩形,处于 OAT++ SERVER(浅蓝色大框,代表服务器端整体环境 )内。它接收来自 CROS 的请求,负责接收和解析请求参数,调用相应的 Service 层方法,并决定返回给客户端的数据格式和内容。是请求进入服务器后的第一站,起到调度和控制流程的作用。
DTO(数据传输对象):蓝色矩形,与 Controller 有交互。用于在不同层之间传输数据,对数据进行封装和转换,保证数据在传输过程中的一致性和规范性,避免直接传递复杂的业务对象。
Service(服务):粉色矩形。主要处理业务逻辑,接收 Controller 的调用请求,进行具体的业务操作,如数据的增删改查等。它可以调用其他组件(如 Other 代表的其他服务或模块)来完成业务功能。
Other(其他组件):黄色矩形,与 Service 有双向交互。代表系统中其他可能被 Service 调用的组件或服务,比如外部 API、数据库访问层、缓存服务等,用于扩展业务功能。
Application(应用):橙色矩形,位于服务器环境内。可理解为应用程序的整体上下文或容器,承载上述各个组件,提供运行时环境,管理组件的生命周期等。

整个架构图体现了一个典型的分层架构思想,从请求的接收到业务逻辑处理,再到数据的传输和响应返回,各组件分工明确,协同完成 Web 应用程序的功能。

二:

DOT数据传输对象

(1)DTO 基础概念

DTO(Data Transfer Object)是一种设计模式,用于在不同层(如客户端与服务器、服务与服务之间)传输数据。

在 Oat++ 中,DTO 是实现数据序列化和反序列化的核心组件,它提供了以下优势:

数据结构标准化:定义清晰的接口规范

安全的数据交换:避免直接暴露业务对象

性能优化:通过选择性序列化减少传输数据量

类型安全:编译时类型检查

跨语言兼容性:支持多种格式(JSON、XML 等)

(2)DTO 定义与结构

2.1基本 DTO 定义

在 Oat++ 中,DTO 是通过继承 oatpp::DTO 并使用代码生成宏来定义的:

#include "oatpp/core/macro/codegen.hpp"
#include "oatpp/core/Types.hpp"/* 开始 DTO 代码生成 */
#include OATPP_CODEGEN_BEGIN(DTO)/*** 用户数据传输对象*/
class UserDto : public oatpp::DTO {DTO_INIT(UserDto, DTO)DTO_FIELD(Int32, id);                // 整数类型字段DTO_FIELD(String, name, "user-name"); // 字符串类型字段,指定 JSON 字段名DTO_FIELD(Boolean, active);         // 布尔类型字段DTO_FIELD(DateTime, created);       // 日期时间类型字段};/* 结束 DTO 代码生成 */
#include OATPP_CODEGEN_END(DTO)

 2.2字段属性详解

基本数据类型。Oat++ 支持多种基本数据类型:

DTO_FIELD(Int8, int8Field); // 8位整数

DTO_FIELD(Int16, int16Field); // 16位整数

DTO_FIELD(Int32, int32Field); // 32位整数

DTO_FIELD(Int64, int64Field); // 64位整数

DTO_FIELD(Float32, float32Field); // 32位浮点数

DTO_FIELD(Float64, float64Field); // 64位浮点数

DTO_FIELD(Boolean, boolField); // 布尔值

DTO_FIELD(String, stringField); // 字符串

DTO_FIELD(DateTime, dateTimeField); // 日期时间

复杂数据类型。支持嵌套 DTO 和集合类型:

// 嵌套 DTO DTO_FIELD(Object<AddressDto>, address);

// 集合类型

DTO_FIELD(List<String>, tags);

DTO_FIELD(Fields<String>, metadata);

DTO_FIELD(Vector<Object<ItemDto>>, items); 

字段选项。可以为字段设置默认值、验证规则等:

// 设置默认值

DTO_FIELD(String, language, "lang") = "en";

// 标记为必需字段

DTO_FIELD_REQUIRED(Int32, age);

// 可选字段(默认值为 nullptr)

DTO_FIELD(String, phone);

(3) DTO 高级特性

3.1 自定义序列化 / 反序列化

可以通过 DTO_FIELD_GET 和 DTO_FIELD_SET 宏自定义字段的序列化和反序列化逻辑:

class CustomDto : public oatpp::DTO {DTO_INIT(CustomDto, DTO)DTO_FIELD(String, encryptedField);// 自定义序列化逻辑DTO_FIELD_GET(encryptedField) {// 模拟加密return encrypt(value);}// 自定义反序列化逻辑DTO_FIELD_SET(encryptedField) {// 模拟解密value = decrypt(inValue);}};

3.2 验证与约束

Oat++ 提供了基本的验证功能:

class ValidatedUserDto : public oatpp::DTO {DTO_INIT(ValidatedUserDto, DTO)// 必需字段,最小长度为 3DTO_FIELD_REQUIRED(String, username)->addValidator(Validator::minLength(3));// 邮箱格式验证DTO_FIELD_REQUIRED(String, email)->addValidator(Validator::email());// 年龄范围验证DTO_FIELD_REQUIRED(Int32, age)->addValidator(Validator::min(18))->addValidator(Validator::max(100));};

 3.3 继承与多态

DTO 支持继承,可以创建基类 DTO 和派生类 DTO:

/* 基类 DTO */
class AnimalDto : public oatpp::DTO {DTO_INIT(AnimalDto, DTO)DTO_FIELD(String, type);DTO_FIELD(String, name);};/* 派生类 DTO */
class DogDto : public AnimalDto {DTO_INIT(DogDto, AnimalDto)DTO_FIELD(String, breed);};class CatDto : public AnimalDto {DTO_INIT(CatDto, AnimalDto)DTO_FIELD(Boolean, lazy);};

(4)DTO 与 API 集成

4.1 在 API 中使用 DTO

在控制器中,可以直接使用 DTO 作为请求体和响应体:

ENDPOINT("POST", "user", createUser,BODY_DTO(Object<UserDto>, userDto)) {// 处理用户创建逻辑auto createdUser = userService->createUser(userDto);return createDtoResponse(Status::CODE_201, createdUser);
}ENDPOINT("GET", "user/{id}", getUser,PATH(Int32, id)) {// 获取用户逻辑auto user = userService->getUserById(id);return createDtoResponse(Status::CODE_200, user);
}

4.2 集合类型响应 

返回列表或集合类型:

ENDPOINT("GET", "users", getUsers) {auto users = userService->getAllUsers();return createDtoResponse(Status::CODE_200, users);
}

4.3 分页响应 

class PageDto : public oatpp::DTO {DTO_INIT(PageDto, DTO)DTO_FIELD(Vector<Object<UserDto>>, items);DTO_FIELD(Int32, page);DTO_FIELD(Int32, size);DTO_FIELD(Int32, total);};ENDPOINT("GET", "users", getUsers,QUERY(Int32, page, "page", 0),QUERY(Int32, size, "size", 10)) {auto page = userService->getUsersPage(page, size);return createDtoResponse(Status::CODE_200, page);
}

(5)DTO 与数据库集成 

5.1 DTO 与数据库实体映射

通常需要在 DTO 和数据库实体之间进行映射:

// 数据库实体
class User {
public:int id;std::string name;std::string email;bool active;
};// DTO 到实体的映射
User userDtoToEntity(const std::shared_ptr<UserDto>& dto) {User user;user.id = dto->id;user.name = dto->name->c_str();user.email = dto->email->c_str();user.active = dto->active;return user;
}// 实体到 DTO 的映射
std::shared_ptr<UserDto> userEntityToDto(const User& user) {auto dto = UserDto::createShared();dto->id = user.id;dto->name = user.name;dto->email = user.email;dto->active = user.active;return dto;
}

 5.2 使用 ORM 简化映射

// 定义数据库模式
#include OATPP_CODEGEN_BEGIN(DbClient)class UserDbClient : public oatpp::orm::DbClient {
public:QUERY(createUser,"INSERT INTO User (name, email, active) VALUES (:user.name, :user.email, :user.active);",PARAM(oatpp::Object<UserDto>, user))QUERY(getUserById,"SELECT * FROM User WHERE id=:id;",PARAM(Int32, id))};#include OATPP_CODEGEN_END(DbClient)

 (6)DTO 最佳实践

6.1 命名规范

使用清晰的命名,避免缩写(如 userId 而非 uid

保持与数据库字段或前端约定一致

使用 PascalCase 命名 DTO 类(如 UserProfileDto

6.2 字段设计原则

仅包含需要传输的数据

避免敏感信息(如密码哈希)

使用有意义的字段名,避免技术术语

考虑未来扩展性,预留字段

6.3 性能优化

避免过度嵌套,保持扁平化结构

使用集合类型代替重复嵌套

对于大数据量,考虑分页或流式传输

使用字段组选择性序列化(如仅返回必要字段)

(7)常见问题与解决方案

7.1 序列化 / 反序列化错误

  • 问题:JSON 字段名与 DTO 字段名不匹配

  • 解决方案:使用 DTO_FIELD 的第三个参数指定 JSON 字段名

  • 问题:类型不匹配

  • 解决方案:确保 DTO 字段类型与 JSON 数据类型一致

7.2 嵌套 DTO 处理

问题:复杂嵌套结构导致性能问题

解决方案:考虑扁平化设计或使用单独的 API 获取嵌套数据

问题:循环引用

解决方案:使用 ID 引用代替直接嵌套,或在序列化时忽略循环引用字段

7.3 验证失败

问题:验证规则不满足

解决方案:检查验证规则,确保数据符合要求

进阶方案:自定义验证器处理复杂业务规则

ENDPOINT

ENDPOINT 是 OAT++ 框架中用于定义 HTTP API 端点(路由) 的核心机制。它通过声明式语法将 HTTP 请求映射到具体的处理函数,并自动完成参数绑定、数据序列化/反序列化、错误处理等流程。以下是对其作用的详细讲解:

(1)ENDPOINT 的核心作用

1、定义路由规则

将特定的 HTTP 方法(如 GET、POST)和 URL 路径(如 /users/{id})绑定到一个处理函数。

示例:ENDPOINT("GET", "/users", getUsers) 表示当客户端发送 GET 请求到 /users 时,调用 getUsers 函数处理。

2、参数自动绑定

自动从 HTTP 请求中提取参数(路径参数、查询参数、请求体等),并转换为 C++ 类型或 DTO 对象。

示例:PATH(Int64, id) 自动将 URL 路径中的 {id} 转换为 int64_t 类型。

3、标准化响应生成

通过 createResponse 生成符合 HTTP 规范的响应(状态码、头部、数据体),并自动将 C++ 对象序列化为 JSON 等格式。

4、依赖注入与中间件支持

支持注入全局组件(如数据库连接池、日志服务),并可集成中间件(如身份验证、CORS 处理)。

(2)ENDPOINT 的语法结构

在 OAT++ 中,ENDPOINT 是一个宏(Macro),其基本语法如下:

ENDPOINT(
    HTTP方法,       // 如 "GET", "POST", "PUT", "DELETE"
    URL路径,        // 如 "/users", "/users/{id}"
    处理函数名,      // 自定义的函数名,如 getUserById
    参数绑定列表...  // 如 PATH(...), QUERY(...), BODY_DTO(...)
) {
    // 业务逻辑代码
    return createResponse(...); // 生成HTTP响应
}

(3)执行流程 

  1. 请求接收:OAT++ Server 监听并解析 HTTP 请求。

  2. 路由匹配:根据 URL 和方法匹配到对应的 ENDPOINT 处理函数。

  3. 参数绑定:自动提取路径、查询等参数,并进行类型转换与校验。

  4. 业务执行:控制器调用服务层处理逻辑,可能涉及数据库或外部服务。

  5. 响应生成:将结果序列化为标准格式(如 JSON),附加头部并返回客户端。

(4)ENDPOINT的关键特性

(一)支持的 HTTP 方法

所有标准方法:GETPOSTPUTDELETEPATCHHEADOPTIONS

示例:ENDPOINT("POST", "/users", createUser, BODY_DTO(...))

(二)参数绑定类型

参数类型示例
路径参数PATH(Type, Name)PATH(Int64, id)
查询参数QUERY(Type, Name)QUERY(String, name)
请求体(DTO)BODY_DTO(...)BODY_DTO(UserDto, user)
HTTP 头部HEADER(Type, Name)HEADER(String, authToken)
表单字段FORM_FIELD(...)FORM_FIELD(File, avatar)

 (三) 自动错误处理

类型不匹配:若客户端传递的参数无法转换为声明类型(如将字符串传给 Int64),OAT++ 自动返回 400 Bad Request

参数缺失:若未提供必需参数(如未指定 PATH 参数),返回 404 Not Found 或 400 Bad Request

(四)依赖注入

通过 OATPP_COMPONENT 注入全局依赖(如数据库连接、配置对象):

ENDPOINT("GET", "/users", getAllUsers,
         // 注入数据库组件
         OATPP_COMPONENT(std::shared_ptr<Database>, db)) {
  auto users = db->queryAllUsers();
  return createResponse(Status::CODE_200, users);
}

(5)最佳实践与注意事项

(一)保持Controller轻量

在 ENDPOINT 中仅处理参数绑定和响应生成,业务逻辑应交给 Service 层。

(二)合理使用DTO

对于复杂请求体,优先通过 DTO 定义数据结构,确保类型安全和可维护性。

(三)统一错误响应

使用 createResponse 返回标准化的错误格式:

return createResponse(Status::CODE_404, "User not found");

(四)路由分组与模块化 

按功能将端点分组到不同的 Controller 类中(如 UserControllerOrderController

(五)性能优化

避免在 ENDPOINT 中进行阻塞操作(如复杂计算),使用异步任务或线程池处理耗时逻辑。

API Controller

Oat++ API 控制器详解:构建强大的 RESTful 服务

(1)API 控制器基础概念

1.1什么是API 控制器?

在 Oat++ 中,API 控制器 是处理 HTTP 请求的核心组件,它负责:

1、定义 API 端点的路径、方法和参数

2、接收客户端请求并解析数据

3、调用相应的业务逻辑

4、返回格式化的响应给客户端

控制器通过继承 oatpp::web::server::api::ApiController 并使用代码生成宏来定义,使代码简洁且类型安全。

1.2 控制器与 MVC 模式

在 MVC(模型 - 视图 - 控制器)架构中,Oat++ 的控制器对应 MVC 中的 "C" 角色:

模型(Model):DTO 和业务逻辑

视图(View):通常由客户端处理,Oat++ 专注于数据返回

控制器(Controller):处理请求和响应

这种分离使代码结构清晰,易于维护和测试。

(2)控制器的基本结构

2.1 定义控制器类

#include "oatpp/web/server/api/ApiController.hpp"
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "oatpp/core/macro/codegen.hpp"/* 开始控制器代码生成 */
#include OATPP_CODEGEN_BEGIN(ApiController)/*** 示例 API 控制器*/
class ExampleController : public oatpp::web::server::api::ApiController {
public:/*** 构造函数,需要传入 ObjectMapper 用于序列化/反序列化*/ExampleController(const std::shared_ptr<ObjectMapper>& objectMapper): oatpp::web::server::api::ApiController(objectMapper){}/* 端点定义将在这里 */};/* 结束控制器代码生成 */
#include OATPP_CODEGEN_END(ApiController)

2.2 注册控制器到路由器 

// 创建路由器
auto router = HttpRouter::createShared();// 创建 ObjectMapper
auto objectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared();// 创建并注册控制器
auto controller = ExampleController::createShared(objectMapper);
controller->addEndpointsToRouter(router);

(3)端点(Endpoint)定义

上一节已介绍并讲解了Endpoint,此处不再赘述。

(4)请求处理与响应

4.1 请求对象

可以直接访问完整的请求对象:

ENDPOINT("GET", "request-info", getRequestInfo,REQUEST(std::shared_ptr<IncomingRequest>, request)) {auto path = request->getPath();auto headers = request->getHeaders();auto method = request->getMethod();// 构建响应auto responseDto = RequestInfoDto::createShared();responseDto->path = path;responseDto->method = method;responseDto->headers = headers;return createDtoResponse(Status::CODE_200, responseDto);
}

4.2 响应类型 

4.2.1 简单文本响应

return createResponse(Status::CODE_200, "Plain text response");

4.2.2 DTO 响应

return createDtoResponse(Status::CODE_200, userDto); 

4.2.3 自定义响应头

auto response = createResponse(Status::CODE_200, "Custom header response"); response->putHeader("X-Custom-Header", "Custom Value");

return response; 

4.2.4 文件响应 

auto response = createResponse(Status::CODE_200, "File content");

response->putHeader(Header::CONTENT_TYPE, "application/octet-stream");

response->putHeader(Header::CONTENT_DISPOSITION, "attachment; filename=example.txt");

return response;

4.2.5 错误响应

return createResponse(Status::CODE_404, "Resource not found"); 

(5)中间件与拦截器 

5.1 请求拦截器

可以为控制器或特定端点添加拦截器:

class AuthInterceptor : public oatpp::web::server::handler::RequestInterceptor {
public:std::shared_ptr<OutgoingResponse> intercept(const std::shared_ptr<IncomingRequest>& request) override {auto authHeader = request->getHeader("Authorization");if(!authHeader || !authService->validateToken(authHeader)) {return ResponseFactory::createResponse(Status::CODE_401, "Unauthorized");}// 将用户信息添加到请求上下文中request->putBundleData("userId", authService->getUserIdFromToken(authHeader));return nullptr; // 继续处理请求}};

 5.2 注册拦截器

// 为控制器添加全局拦截器
ENDPOINT("GET", "protected", getProtected,INTERCEPTOR(AuthInterceptor, authInterceptor)) {// 只有通过认证的请求才能到达这里auto userId = request->getBundleData("userId");return createResponse(Status::CODE_200, "Access granted");
}// 为控制器所有端点添加全局拦截器
void ExampleController::addEndpointsToRouter(const std::shared_ptr<HttpRouter>& router) {auto authInterceptor = std::make_shared<AuthInterceptor>();// 为所有端点添加拦截器router->addGlobalRequestInterceptor(authInterceptor);// 添加端点ApiController::addEndpointsToRouter(router);
}

(6)错误处理

6.1 自定义错误处理器

class CustomErrorHandler : public oatpp::web::server::handler::ErrorHandler {
public:std::shared_ptr<OutgoingResponse> handleError(const Status& status,const oatpp::String& message,const Headers& headers) override {auto errorDto = ErrorDto::createShared();errorDto->statusCode = status.code;errorDto->message = message;auto response = createDtoResponse(status, errorDto);// 添加额外的响应头for(auto& pair : headers.getAll()) {response->putHeader(pair.first.toString(), pair.second.toString());}return response;}};

6.2 注册错误处理器

// 创建并设置错误处理器
auto errorHandler = std::make_shared<CustomErrorHandler>();
serverConnectionHandler->setErrorHandler(errorHandler);

6.3 在控制器中抛出错误

ENDPOINT("GET", "user/{id}", getUser,PATH(Int32, id)) {auto user = userService->getUserById(id);if(!user) {throw oatpp::web::protocol::http::HttpError(Status::CODE_404,"User not found");}return createDtoResponse(Status::CODE_200, user);
}

(7)控制器组织与最佳实践

7.1 按业务功能拆分控制器

// 项目结构示例
src/
├── controller/
│   ├── UserController.hpp  // 用户相关 API
│   ├── ProductController.hpp  // 产品相关 API
│   └── OrderController.hpp  // 订单相关 API

7.2 使用服务层分离业务逻辑 

class UserController : public ApiController {
private:std::shared_ptr<UserService> m_userService;
public:UserController(const std::shared_ptr<ObjectMapper>& objectMapper,const std::shared_ptr<UserService>& userService): ApiController(objectMapper), m_userService(userService){}ENDPOINT("GET", "user/{id}", getUser,PATH(Int32, id)) {return createDtoResponse(Status::CODE_200, m_userService->getUserById(id));}};

7.3 版本控制 

// V1 控制器
class UserControllerV1 : public ApiController {// 旧版本 API
};// V2 控制器
class UserControllerV2 : public ApiController {// 新版本 API
};// 注册时区分路径
v1Router->addController(std::make_shared<UserControllerV1>(objectMapper));
v2Router->addController(std::make_shared<UserControllerV2>(objectMapper));

7.4 代码复用与基类 

class BaseController : public ApiController {
protected:std::shared_ptr<AuthService> m_authService;bool isAuthenticated(const std::shared_ptr<IncomingRequest>& request) {// 认证逻辑}std::shared_ptr<UserDto> getCurrentUser(const std::shared_ptr<IncomingRequest>& request) {// 获取当前用户}
};class UserController : public BaseController {// 继承认证和用户获取功能
};

相关文章:

  • 如何查看打开的 git bash 窗口是否是管理员权限打开
  • EasyRTC嵌入式音视频通信SDK打造带屏IPC全场景实时通信解决方案
  • 全新开发-iVX图形化编程VS完整IDE
  • 正向代理与反向代理区别及应用
  • React学习———useContext和useReducer
  • 深度伪造对知识产权保护的新挑战与应对之策
  • 天拓四方盛装亮相第二十七届中国北京国际科技产业博览会
  • Colorama:Python终端色彩美化从入门到高级
  • 网络检测工具InternetTest v8.9.1.2504 单文件版,支持一键查询IP/DNS、WIFI密码信息
  • SVM在医疗设备故障维修服务决策中的应用:策略、技术与实践
  • c++STL——哈希表封装:实现高效unordered_map与unordered_set
  • 现代计算机图形学Games101入门笔记(八)
  • 从构想到交付:专业级软开发流程详解
  • 深度剖析LLM的“大脑”:单层Transformer的思考模式探索
  • JavaScript判断数据的类型
  • 配置wsl内核时出现Multimedia support下面没选项
  • 大疆无人机自主飞行解决方案局限性及增强解决方案-AIBOX:特色行业无人机巡检解决方案
  • Day24-元组、OS模块
  • 如何在Mac电脑上的VScode去配置C/C++环境
  • redis 命令大全整理
  • 晋级四强!WTA1000罗马站:郑钦文2比0萨巴伦卡
  • GDP逼近五千亿,向海图强,对接京津,沧州剑指沿海经济强市
  • 共情场域与可持续发展——关于博物馆、美术馆运营的新思考
  • 极限拉扯上任巴西,安切洛蒂开启夏窗主帅大挪移?
  • 哈佛新论文揭示 Transformer 模型与人脑“同步纠结”全过程!AI也会犹豫、反悔?
  • 淡马锡辟谣:淡马锡和太白投资未在中国销售任何投资产品或金融工具