Ota++框架学习

news2025/5/15 11:55:36

一:框架结构

这是一幅展现 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)
  
  // 必需字段,最小长度为 3
  DTO_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 {
  // 继承认证和用户获取功能
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2376073.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Chrome安装最新vue-devtool插件

本vue-devtool版本是官方的 v7.6.8版本&#xff0c;兼容性好、功能齐全且稳定。 操作步骤&#xff1a; 方法一&#xff1a; 打开谷歌浏览器 --> 右上角三个点 --> 扩展程序 --> 管理扩展程序 --> 加载已解压的扩展程序&#xff0c; 然后选择解压后的文件夹即可。…

bfs-最小步数问题

最小步长模型 特征&#xff1a; 主要是解决权值为1且状态为字符串类型的最短路问题&#xff0c;实质上是有向图的最短路问题&#xff0c;可以简化为bfs求最短路问题。 代表题目&#xff1a; acwing 845 八数码问题&#xff1a; 八数码题中由于每次交换的状态是由x进行上下左右…

java----------->代理模式

目录 什么是代理模式&#xff1f; 为什么会有代理模式&#xff1f; 怎么写代理模式&#xff1f; 实现代理模式总共需要三步&#xff1a; 什么是代理模式&#xff1f; 代理模式&#xff1a;给目标对象提供一个代理对象&#xff0c;并且由代理对象控制目标对象的引用 代理就是…

Untiy基础学习(十四)核心系统—物理系统之碰撞检测代码篇 刚体,碰撞体,材质

目录 一、碰撞器&#xff08;Collider&#xff09;与触发器&#xff08;Trigger&#xff09; 二、碰撞检测条件 三、碰撞事件与触发器事件&#xff0c;可以理解为特殊的生命周期函数。 四、讲讲如何选择 ​编辑 五、总结 一、碰撞/触发事件函数对照表 二、Collider 与 …

SAP学习笔记 - 开发08 - Eclipse连接到 BTP Cockpit实例

有关BTP&#xff0c;之前学了一点儿&#xff0c;今天继续学习。 SAP学习笔记 - 开发02 - BTP实操流程&#xff08;账号注册&#xff0c;BTP控制台&#xff0c;BTP集成开发环境搭建&#xff09;_sap btp开发-CSDN博客 如何在Eclipse中连接BTP Cockpit开发环境实例。 1&#xf…

Git的安装和配置(idea中配置Git)

一、Git的下载和安装 前提条件&#xff1a;IntelliJ IDEA 版本是2023.3 &#xff0c;那么配置 Git 时推荐使用 Git 2.40.x 或更高版本 下载地址&#xff1a;CNPM Binaries Mirror 操作&#xff1a;打开链接 → 滚动到页面底部 → 选择2.40.x或更高版本的 .exe 文件&#xf…

【2025版】Spring Boot面试题

文章目录 1. Spring, Spring MVC, SpringBoot是什么关系&#xff1f;2. 谈一谈对Spring IoC的理解3. Component 和 Bean 的区别&#xff1f;4. Autowired 和 Resource 的区别&#xff1f;5. 注入Bean的方法有哪些&#xff1f;6. 为什么Spring 官方推荐构造函数注入&#xff1f;…

火山引擎实时音视频 高代码跑通日志

实时音视频 SDK 概览--实时音视频-火山引擎 什么是实时音视频 火山引擎实时音视频&#xff08;Volcengine Real Time Communication&#xff0c;veRTC&#xff09;提供全球范围内高可靠、高并发、低延时的实时音视频通信能力&#xff0c;实现多种类型的实时交流和互动。 通…

jenkins 启动报错

java.lang.UnsatisfiedLinkError: /opt/application/jdk-17.0.11/lib/libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory。 解决方案&#xff1a; yum install freetype-devel 安装完成之后重启jenkins。

【合新通信】无人机天线拉远RFOF(射频光纤传输)解决方案

无人机天线拉远RFOF方案通过光纤替代传统射频电缆&#xff0c;实现无人机与地面控制站之间的高保真、低损耗信号传输&#xff0c;尤其适用于高频段&#xff08;如毫米波&#xff09;、远距离或复杂电磁环境下的无人机作业场景。 核心应用场景 军事侦察与电子战 隐蔽部署&…

程序设计语言----软考中级软件设计师(自用学习笔记)

目录 1、解释器和编译器 2、程序的三种控制结构 3、程序中的数据必须具有类型 4、编译、解释程序翻译阶段 5、符号表 6、编译过程 7、上下文无关文法 8、前、中、后缀表达式 9、前、后缀表达式计算 10、语法树中、后序遍历 11、脚本语言和动态语言 12、语法分析方法…

通过SMTP协议实现Linux邮件发送配置指南

一、环境准备与基础配置 1. SMTP服务开通&#xff08;以qq邮箱为例&#xff09; 登录qq邮箱网页端&#xff0c;进入「设置」-「POP3/SMTP/IMAP」 开启「SMTP服务」并获取16位授权码&#xff08;替代邮箱密码使用&#xff09; 记录关键参数&#xff1a; SMTP服务器地址&#…

若依框架页面

1.页面地址 若依管理系统 2.账号和密码 管理员 账号admin 密码admin123 运维 账号yuwei 密码123456 自己搭建的地址方便大家学习&#xff0c;不要攻击哦&#xff0c;谢谢啊

44、私有程序集与共享程序集有什么区别?

私有程序集&#xff08;Private Assembly&#xff09;与共享程序集&#xff08;Shared Assembly&#xff09;是.NET框架中程序集部署的两种不同方式&#xff0c;它们在部署位置、版本控制、访问权限等方面存在显著差异&#xff0c;以下是对二者的详细比较&#xff1a; 1. 部署…

【Java面试题】——this 和 super 的区别

&#x1f381;个人主页&#xff1a;User_芊芊君子 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 &#x1f50d;系列专栏&#xff1a;【Java】内容概括 【前言】 在Java的世界里&#xff0c;this和 super是两个非常重要且容易混淆的关键字。无论是在日常…

CentOS 7 内核升级指南:解决兼容性问题并提升性能

点击上方“程序猿技术大咖”&#xff0c;关注并选择“设为星标” 回复“加群”获取入群讨论资格&#xff01; CentOS 7 默认搭载的 3.10.x 版本内核虽然稳定&#xff0c;但随着硬件和软件技术的快速发展&#xff0c;可能面临以下问题&#xff1a; 硬件兼容性不足&#xff1a;新…

解决 PicGo 上传 GitHub图床及Marp中Github图片编译常见难题指南

[目录] 0.行文概述 1.PicGo图片上传失败 2.*关于在Vscode中Marp图片的编译问题* 3.总结与启示行文概述 写作本文的动机是本人看到了Awesome Marp&#xff0c;发现使用 Markdown \texttt{Markdown} Markdown做PPT若加持一些 CSS , JavaScript \texttt{CSS},\texttt{JavaScript} …

软考 系统架构设计师系列知识点之杂项集萃(59)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;58&#xff09; 第96题 假设关系模式R(U, F)&#xff0c;属性集U{A, B, C}&#xff0c;函数依赖集F{A->B, B->C}。若将其分解为p{R1(U1, F1), R2(U2, F2)&#xff0c;其中U1{A, B}, U2{A, …

python使用matplotlib画图

【README】 plot画图有两种方法&#xff1a;包括 plt.plot(), ax.plot()-画多个子图 &#xff0c;其中ax表示某个坐标轴; 【1】画单个图 import matplotlib # 避免兼容性问题&#xff1a;明确指定 matplotlib 使用兼容的后端&#xff0c;TkAgg 或 Agg matplotlib.use(TkAgg) …

upload-labs通关笔记-第5关 文件上传之.ini绕过

目录 一、ini文件绕过原理 二、源码审计 三、渗透实战 1、查看提示 2、制作.user.ini文件 &#xff08;1&#xff09;首先创建一个文本文件 &#xff08;2&#xff09;保存文件名为.user.ini 2、制作jpg后缀脚本 &#xff08;1&#xff09;创建一个文本文件 &#xf…