cpp-httplib RESTful API设计指南:构建现代化Web服务

cpp-httplib RESTful API设计指南:构建现代化Web服务

【免费下载链接】cpp-httplib A C++ header-only HTTP/HTTPS server and client library 【免费下载链接】cpp-httplib 项目地址: https://gitcode.com/gh_mirrors/cp/cpp-httplib

1. 痛点与解决方案

你是否在寻找一个轻量级、无依赖且高效的C++ HTTP库来构建RESTful API?cpp-httplib作为一个仅需包含头文件的库,完美解决了传统C++ Web开发中依赖复杂、配置繁琐的问题。本文将从基础架构到高级功能,全面讲解如何使用cpp-httplib构建符合现代标准的RESTful API服务。

读完本文后,你将能够:

  • 快速搭建高性能C++ RESTful服务
  • 实现完整的CRUD操作与路由管理
  • 处理JSON请求与响应数据
  • 实现认证授权与中间件机制
  • 优化服务性能与可扩展性
  • 构建完整的API测试与文档系统

2. cpp-httplib架构概览

2.1 核心组件

cpp-httplib采用简洁的架构设计,主要包含以下核心组件:

mermaid

2.2 工作流程

cpp-httplib处理HTTP请求的工作流程如下:

mermaid

3. 环境配置与快速启动

3.1 环境要求

cpp-httplib对环境有以下要求:

环境最低版本要求
C++标准C++11及以上
编译器GCC 4.8+, Clang 3.5+, MSVC 2015+
操作系统Windows 10+, Linux, macOS
可选依赖OpenSSL (HTTPS支持), zlib (压缩支持)

3.2 项目配置

使用cpp-httplib只需在项目中包含头文件即可:

#include <httplib.h>
using namespace httplib;

对于CMake项目,可以添加以下配置:

cmake_minimum_required(VERSION 3.10)
project(my_rest_api)

set(CMAKE_CXX_STANDARD 11)

# 添加cpp-httplib
include_directories(path/to/cpp-httplib)

# 如果需要HTTPS支持
find_package(OpenSSL REQUIRED)
target_link_libraries(my_rest_api OpenSSL::SSL OpenSSL::Crypto)

add_executable(my_rest_api main.cpp)

3.3 第一个REST服务

以下是一个简单的RESTful API服务示例:

#include <httplib.h>
using namespace httplib;

int main() {
    // 创建HTTP服务器
    Server svr;

    // 定义GET路由
    svr.Get("/api/hello", [](const Request& req, Response& res) {
        res.set_content("Hello, RESTful API!", "text/plain");
    });

    // 启动服务器
    std::cout << "Server starting on http://localhost:8080" << std::endl;
    svr.listen("localhost", 8080);
    
    return 0;
}

编译并运行后,使用curl测试:

curl http://localhost:8080/api/hello
# 输出: Hello, RESTful API!

4. 路由系统详解

4.1 基础路由定义

cpp-httplib支持标准HTTP方法的路由定义:

// GET请求
svr.Get("/api/users", [](const Request& req, Response& res) {
    // 获取所有用户
});

// POST请求
svr.Post("/api/users", [](const Request& req, Response& res) {
    // 创建新用户
});

// PUT请求
svr.Put("/api/users/:id", [](const Request& req, Response& res) {
    // 更新用户
});

// DELETE请求
svr.Delete("/api/users/:id", [](const Request& req, Response& res) {
    // 删除用户
});

4.2 路径参数与查询参数

处理路径参数和查询参数:

// 路径参数示例
svr.Get("/api/users/:id", [](const Request& req, Response& res) {
    // 获取路径参数
    auto user_id = req.path_params["id"];
    
    // 处理请求...
    res.set_content("User ID: " + user_id, "text/plain");
});

// 查询参数示例
svr.Get("/api/products", [](const Request& req, Response& res) {
    // 获取查询参数
    auto category = req.get_param_value("category");
    auto page = req.get_param_value("page", "1"); // 带默认值
    
    // 处理请求...
    res.set_content("Category: " + category + ", Page: " + page, "text/plain");
});

4.3 正则表达式路由

使用正则表达式匹配复杂路由:

// 匹配日期格式的路径
svr.Get(R"(/api/logs/(\d{4}-\d{2}-\d{2}))", [](const Request& req, Response& res) {
    auto date = req.matches[1]; // 获取匹配的日期
    res.set_content("Log date: " + date.str(), "text/plain");
});

// 匹配多种文件类型的路由
svr.Get(R"(/files/(\w+)\.(jpg|png|gif))", [](const Request& req, Response& res) {
    auto filename = req.matches[1].str();
    auto ext = req.matches[2].str();
    res.set_content("File: " + filename + "." + ext, "text/plain");
});

4.4 路由优先级与冲突解决

cpp-httplib采用先定义先匹配的路由策略,处理路由冲突的最佳实践:

// 具体路由应先定义
svr.Get("/api/users/profile", [](const Request& req, Response& res) {
    res.set_content("User Profile", "text/plain");
});

// 通配符路由应后定义
svr.Get("/api/users/:id", [](const Request& req, Response& res) {
    res.set_content("User ID: " + req.path_params["id"], "text/plain");
});

// 404处理
svr.set_not_found_handler([](const Request& req, Response& res) {
    res.status = 404;
    res.set_content("{\"error\":\"Resource not found\"}", "application/json");
});

5. 请求与响应处理

5.1 请求头与响应头处理

// 处理请求头
svr.Get("/api/headers", [](const Request& req, Response& res) {
    // 检查请求头
    if (req.has_header("Authorization")) {
        auto token = req.get_header_value("Authorization");
        // 验证token...
    }
    
    // 设置响应头
    res.set_header("X-API-Version", "1.0.0");
    res.set_header("Content-Type", "application/json");
    
    res.set_content("{\"status\":\"success\"}", "application/json");
});

5.2 JSON数据处理

处理JSON请求和响应(需要JSON库支持,如nlohmann/json):

#include <nlohmann/json.hpp>
using json = nlohmann::json;

// 处理JSON请求
svr.Post("/api/users", [](const Request& req, Response& res) {
    try {
        // 解析JSON请求体
        json req_body = json::parse(req.body);
        
        // 处理数据
        std::string name = req_body["name"];
        int age = req_body["age"];
        
        // 构建响应
        json res_body;
        res_body["status"] = "success";
        res_body["message"] = "User created";
        res_body["data"]["id"] = 123;
        res_body["data"]["name"] = name;
        
        res.set_content(res_body.dump(4), "application/json");
        res.status = 201; // Created
    } catch (const json::exception& e) {
        res.status = 400; // Bad Request
        res.set_content("{\"error\":\"Invalid JSON: " + std::string(e.what()) + "\"}", 
                       "application/json");
    }
});

5.3 文件上传处理

处理文件上传:

svr.Post("/api/upload", [](const Request& req, Response& res) {
    // 检查是否是multipart/form-data请求
    if (req.is_multipart_form_data()) {
        // 获取上传的文件
        auto files = req.get_files("file");
        
        json response;
        response["files_count"] = files.size();
        
        for (const auto& file : files) {
            json file_info;
            file_info["name"] = file.filename;
            file_info["size"] = file.content.size();
            file_info["type"] = file.content_type;
            
            // 保存文件
            std::ofstream ofs("uploads/" + file.filename, std::ios::binary);
            ofs << file.content;
            
            response["files"].push_back(file_info);
        }
        
        res.set_content(response.dump(4), "application/json");
    } else {
        res.status = 400;
        res.set_content("{\"error\":\"Expected multipart/form-data\"}", "application/json");
    }
});

5.4 流式响应与大文件处理

处理大文件传输:

// 大文件下载
svr.Get("/api/download/largefile", [](const Request& req, Response& res) {
    // 设置内容提供者
    res.set_content_provider(
        // 文件大小
        1024 * 1024 * 100, // 100MB
        "application/octet-stream",
        // 内容提供者函数
        [](size_t offset, size_t length, DataSink& sink) {
            // 打开文件
            static std::ifstream ifs("large_file.bin", std::ios::binary);
            
            ifs.seekg(offset);
            char buffer[4096];
            size_t remaining = length;
            
            while (remaining > 0) {
                size_t bytes_to_read = std::min(remaining, sizeof(buffer));
                ifs.read(buffer, bytes_to_read);
                sink.write(buffer, bytes_to_read);
                remaining -= bytes_to_read;
            }
            
            return true;
        },
        // 资源释放函数
        [](bool success) {
            // 关闭文件等清理工作
        }
    );
    
    res.set_header("Content-Disposition", "attachment; filename=\"large_file.bin\"");
});

6. 中间件与高级功能

6.1 日志中间件

实现请求日志记录:

// 日志中间件
void log_middleware(const Request& req, Response& res, const std::function<void()>& next) {
    // 请求处理前
    auto start_time = std::chrono::high_resolution_clock::now();
    
    // 调用下一个处理器
    next();
    
    // 请求处理后
    auto end_time = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
    
    // 记录日志
    std::cout << "[" << req.method << "] " << req.path << " " 
              << res.status << " " << duration.count() << "µs" << std::endl;
}

// 应用中间件
svr.Get("/api/*", [&](const Request& req, Response& res) {
    log_middleware(req, res, [&]() {
        // 实际处理逻辑
        res.set_content("{\"status\":\"success\"}", "application/json");
    });
});

6.2 认证中间件

实现JWT认证中间件:

bool authenticate(const Request& req) {
    if (req.has_header("Authorization")) {
        std::string auth_header = req.get_header_value("Authorization");
        // 检查Bearer token格式
        if (auth_header.substr(0, 7) == "Bearer ") {
            std::string token = auth_header.substr(7);
            // 验证token...
            return true; // 验证成功返回true
        }
    }
    return false;
}

// 受保护的路由
svr.Get("/api/protected", [](const Request& req, Response& res) {
    if (!authenticate(req)) {
        res.status = 401; // Unauthorized
        res.set_content("{\"error\":\"Authentication required\"}", "application/json");
        return;
    }
    
    // 处理受保护资源...
    res.set_content("{\"data\":\"Protected content\"}", "application/json");
});

6.3 CORS支持

实现跨域资源共享(CORS):

void enable_cors(Response& res) {
    res.set_header("Access-Control-Allow-Origin", "*");
    res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
    res.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization");
    res.set_header("Access-Control-Max-Age", "86400");
}

// 处理OPTIONS请求
svr.Options("/*", [](const Request& req, Response& res) {
    enable_cors(res);
    res.status = 204; // No Content
});

// 为所有API路由添加CORS头
svr.Get("/api/*", [](const Request& req, Response& res) {
    enable_cors(res);
    // 处理请求...
});

6.4 错误处理机制

全局错误处理:

// 设置错误处理器
svr.set_error_handler([](const Request& req, Response& res) {
    json error_response;
    error_response["error"] = status_message(res.status);
    error_response["status_code"] = res.status;
    error_response["path"] = req.path;
    
    res.set_content(error_response.dump(4), "application/json");
});

// 自定义异常处理
svr.set_exception_handler([](const Request& req, Response& res, std::exception_ptr ep) {
    try {
        std::rethrow_exception(ep);
    } catch (const std::exception& e) {
        res.status = 500;
        res.set_content("{\"error\":\"" + std::string(e.what()) + "\"}", "application/json");
    }
});

7. 性能优化与安全加固

7.1 连接池与线程管理

cpp-httplib默认使用线程池处理请求,可以根据服务器CPU核心数优化线程池配置:

// 自定义线程池配置
size_t num_threads = std::thread::hardware_concurrency() * 2; // 使用CPU核心数的2倍
svr.new_task_queue = [num_threads] {
    return std::make_shared<ThreadPool>(num_threads);
};

// 设置连接超时
svr.set_timeout(5); // 5秒超时

// 启用TCP_NODELAY
svr.set_socket_options([](socket_t sock) {
    int flag = 1;
    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&flag, sizeof(flag));
});

7.2 HTTPS配置

启用HTTPS支持:

#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
// 创建SSL服务器
SSLServer svr(
    "./cert.pem",          // 证书文件
    "./key.pem",           // 私钥文件
    "./dhparam.pem"        // DH参数文件(可选)
);

// 配置TLS版本
svr.set_ssl_init_handler([](SSL_CTX* ctx) {
    SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
    SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
    
    // 配置密码套件
    SSL_CTX_set_cipher_list(ctx, "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384");
    return true;
});
#else
// 非SSL服务器
Server svr;
#endif

7.3 请求速率限制

实现API请求速率限制:

#include <unordered_map>
#include <chrono>

// 速率限制配置
const int RATE_LIMIT = 60; // 每分钟60个请求
const int RATE_LIMIT_WINDOW = 60; // 时间窗口(秒)

// IP请求计数器
std::unordered_map<std::string, std::pair<int, std::chrono::steady_clock::time_point>> request_counts;
std::mutex count_mutex;

// 速率限制中间件
bool rate_limiter(const Request& req, Response& res) {
    std::lock_guard<std::mutex> lock(count_mutex);
    auto now = std::chrono::steady_clock::now();
    std::string ip = req.remote_addr;
    
    // 检查IP是否在计数器中
    if (request_counts.find(ip) == request_counts.end()) {
        request_counts[ip] = {1, now};
        return true;
    }
    
    // 检查是否在时间窗口内
    auto& [count, start_time] = request_counts[ip];
    auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - start_time).count();
    
    if (elapsed > RATE_LIMIT_WINDOW) {
        // 重置计数器
        count = 1;
        start_time = now;
        return true;
    }
    
    // 检查是否超过限制
    if (count >= RATE_LIMIT) {
        res.status = 429; // Too Many Requests
        res.set_content("{\"error\":\"Rate limit exceeded\"}", "application/json");
        return false;
    }
    
    // 增加计数器
    count++;
    return true;
}

// 应用速率限制
svr.Get("/api/*", [](const Request& req, Response& res) {
    if (!rate_limiter(req, res)) {
        return; // 速率限制触发,不继续处理
    }
    
    // 正常处理请求...
});

8. 完整API示例:用户管理系统

8.1 数据模型定义

// 用户模型
struct User {
    int id;
    std::string name;
    std::string email;
    int age;
    std::string created_at;
};

// 模拟数据库
std::unordered_map<int, User> users;
int next_user_id = 1;
std::mutex users_mutex;

8.2 完整CRUD实现

// 获取所有用户
svr.Get("/api/users", [](const Request& req, Response& res) {
    std::lock_guard<std::mutex> lock(users_mutex);
    json user_list = json::array();
    
    for (const auto& [id, user] : users) {
        user_list.push_back({
            {"id", user.id},
            {"name", user.name},
            {"email", user.email},
            {"age", user.age},
            {"created_at", user.created_at}
        });
    }
    
    res.set_content(user_list.dump(4), "application/json");
});

// 获取单个用户
svr.Get("/api/users/:id", [](const Request& req, Response& res) {
    try {
        std::lock_guard<std::mutex> lock(users_mutex);
        int user_id = std::stoi(req.path_params["id"]);
        
        if (users.find(user_id) == users.end()) {
            res.status = 404;
            res.set_content("{\"error\":\"User not found\"}", "application/json");
            return;
        }
        
        User user = users[user_id];
        json user_data = {
            {"id", user.id},
            {"name", user.name},
            {"email", user.email},
            {"age", user.age},
            {"created_at", user.created_at}
        };
        
        res.set_content(user_data.dump(4), "application/json");
    } catch (const std::exception& e) {
        res.status = 400;
        res.set_content("{\"error\":\"Invalid user ID\"}", "application/json");
    }
});

// 创建用户
svr.Post("/api/users", [](const Request& req, Response& res) {
    try {
        json req_body = json::parse(req.body);
        
        // 验证请求数据
        if (!req_body.contains("name") || !req_body.contains("email")) {
            res.status = 400;
            res.set_content("{\"error\":\"Name and email are required\"}", "application/json");
            return;
        }
        
        // 获取当前时间
        auto now = std::chrono::system_clock::now();
        std::time_t now_time = std::chrono::system_clock::to_time_t(now);
        std::string created_at = std::ctime(&now_time);
        created_at.pop_back(); // 移除换行符
        
        // 创建新用户
        std::lock_guard<std::mutex> lock(users_mutex);
        User new_user = {
            next_user_id++,
            req_body["name"],
            req_body["email"],
            req_body.value("age", 0),
            created_at
        };
        
        users[new_user.id] = new_user;
        
        // 返回创建的用户
        json res_body = {
            {"status", "success"},
            {"data", {
                {"id", new_user.id},
                {"name", new_user.name},
                {"email", new_user.email},
                {"age", new_user.age},
                {"created_at", new_user.created_at}
            }}
        };
        
        res.status = 201; // Created
        res.set_content(res_body.dump(4), "application/json");
    } catch (const json::exception& e) {
        res.status = 400;
        res.set_content("{\"error\":\"Invalid JSON: " + std::string(e.what()) + "\"}", "application/json");
    } catch (const std::exception& e) {
        res.status = 500;
        res.set_content("{\"error\":\"Server error: " + std::string(e.what()) + "\"}", "application/json");
    }
});

// 更新用户
svr.Put("/api/users/:id", [](const Request& req, Response& res) {
    try {
        int user_id = std::stoi(req.path_params["id"]);
        json req_body = json::parse(req.body);
        
        std::lock_guard<std::mutex> lock(users_mutex);
        
        if (users.find(user_id) == users.end()) {
            res.status = 404;
            res.set_content("{\"error\":\"User not found\"}", "application/json");
            return;
        }
        
        // 更新用户信息
        User& user = users[user_id];
        if (req_body.contains("name")) user.name = req_body["name"];
        if (req_body.contains("email")) user.email = req_body["email"];
        if (req_body.contains("age")) user.age = req_body["age"];
        
        json res_body = {
            {"status", "success"},
            {"data", {
                {"id", user.id},
                {"name", user.name},
                {"email", user.email},
                {"age", user.age},
                {"created_at", user.created_at}
            }}
        };
        
        res.set_content(res_body.dump(4), "application/json");
    } catch (const std::invalid_argument&) {
        res.status = 400;
        res.set_content("{\"error\":\"Invalid user ID\"}", "application/json");
    } catch (const json::exception& e) {
        res.status = 400;
        res.set_content("{\"error\":\"Invalid JSON: " + std::string(e.what()) + "\"}", "application/json");
    }
});

// 删除用户
svr.Delete("/api/users/:id", [](const Request& req, Response& res) {
    try {
        int user_id = std::stoi(req.path_params["id"]);
        
        std::lock_guard<std::mutex> lock(users_mutex);
        
        if (users.find(user_id) == users.end()) {
            res.status = 404;
            res.set_content("{\"error\":\"User not found\"}", "application/json");
            return;
        }
        
        users.erase(user_id);
        
        res.set_content("{\"status\":\"success\",\"message\":\"User deleted\"}", "application/json");
    } catch (const std::invalid_argument&) {
        res.status = 400;
        res.set_content("{\"error\":\"Invalid user ID\"}", "application/json");
    }
});

8.3 API测试用例

使用curl测试API:

# 创建用户
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"John Doe","email":"john@example.com","age":30}'

# 获取所有用户
curl http://localhost:8080/api/users

# 获取单个用户
curl http://localhost:8080/api/users/1

# 更新用户
curl -X PUT http://localhost:8080/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{"age":31}'

# 删除用户
curl -X DELETE http://localhost:8080/api/users/1

9. 部署与监控

9.1 Docker容器化

创建Dockerfile:

FROM gcc:11-slim

WORKDIR /app

# 安装依赖
RUN apt-get update && apt-get install -y \
    libssl-dev \
    zlib1g-dev \
    && rm -rf /var/lib/apt/lists/*

# 复制源代码
COPY . .

# 编译应用
RUN g++ -std=c++11 -O2 -o rest_api main.cpp -lssl -lcrypto -lz

# 暴露端口
EXPOSE 8080

# 运行应用
CMD ["./rest_api"]

构建并运行容器:

docker build -t cpp-rest-api .
docker run -p 8080:8080 cpp-rest-api

9.2 性能监控

集成基本性能监控:

// 性能指标
struct Metrics {
    std::atomic<int> total_requests = 0;
    std::atomic<int> status_200 = 0;
    std::atomic<int> status_400 = 0;
    std::atomic<int> status_404 = 0;
    std::atomic<int> status_500 = 0;
    std::atomic<long long> total_duration = 0;
};

Metrics metrics;

// 监控中间件
void metrics_middleware(const Request& req, Response& res, const std::function<void()>& next) {
    auto start_time = std::chrono::high_resolution_clock::now();
    
    // 调用下一个处理器
    next();
    
    // 记录指标
    auto end_time = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
    
    metrics.total_requests++;
    metrics.total_duration += duration;
    
    // 状态码统计
    switch (res.status / 100) {
        case 2: metrics.status_200++; break;
        case 4: 
            if (res.status == 404) metrics.status_404++;
            else metrics.status_400++;
            break;
        case 5: metrics.status_500++; break;
    }
}

// 监控指标端点
svr.Get("/api/metrics", [&](const Request& req, Response& res) {
    json metrics_data;
    metrics_data["total_requests"] = metrics.total_requests.load();
    metrics_data["status_codes"]["200"] = metrics.status_200.load();
    metrics_data["status_codes"]["400"] = metrics.status_400.load();
    metrics_data["status_codes"]["404"] = metrics.status_404.load();
    metrics_data["status_codes"]["500"] = metrics.status_500.load();
    
    if (metrics.total_requests > 0) {
        metrics_data["avg_duration_us"] = metrics.total_duration.load() / metrics.total_requests.load();
    } else {
        metrics_data["avg_duration_us"] = 0;
    }
    
    res.set_content(metrics_data.dump(4), "application/json");
});

10. 总结与扩展

10.1 最佳实践总结

使用cpp-httplib构建RESTful API的最佳实践:

  1. 路由设计:采用资源为中心的URL设计,使用复数名词表示集合资源
  2. 错误处理:统一的错误响应格式,适当的HTTP状态码使用
  3. 性能优化:合理配置线程池,启用TCP_NODELAY,实现连接复用
  4. 安全加固:实现HTTPS、认证授权、请求速率限制和输入验证
  5. 代码组织:将路由处理、业务逻辑和数据访问分离
  6. 监控与日志:实现全面的请求日志和性能指标监控
  7. 文档与测试:为API编写详细文档和自动化测试用例

10.2 高级功能探索

cpp-httplib还有许多高级功能值得探索:

  • WebSocket支持:实现实时通信功能
  • 异步请求处理:提高并发处理能力
  • 压缩传输:启用gzip/brotli压缩减少带宽使用
  • HTTP/2支持:提升连接效率和吞吐量
  • 国际化支持:处理多语言和本地化内容

10.3 学习资源

深入学习cpp-httplib的资源:

  • 官方GitHub仓库:https://gitcode.com/gh_mirrors/cp/cpp-httplib
  • 官方示例代码:example目录下的各种示例程序
  • C++ RESTful API设计模式与实践
  • HTTP协议规范与最佳实践

11. 结语

cpp-httplib作为一个轻量级、高性能的C++ HTTP库,为构建RESTful API提供了简洁而强大的工具。通过本文的指南,你已经掌握了从基础路由到高级功能的实现方法,能够构建出健壮、高效且安全的API服务。

随着项目的发展,cpp-httplib也在不断更新完善,持续关注其最新特性将帮助你构建更加现代化的Web服务。现在就开始使用cpp-httplib,体验C++ Web开发的高效与乐趣吧!

如果觉得本文对你有帮助,请点赞、收藏并关注更多C++ Web开发相关内容。下期我们将探讨如何使用cpp-httplib构建WebSocket实时通信服务,敬请期待!

【免费下载链接】cpp-httplib A C++ header-only HTTP/HTTPS server and client library 【免费下载链接】cpp-httplib 项目地址: https://gitcode.com/gh_mirrors/cp/cpp-httplib

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值