STTNet 0.7.0

Demo:HTTP 请求与 JSON

可构建、可运行、可验证,并与仓库 examples 目录中的源码保持一致。

1. 示例内容

完整源码中的关键调用均有中文注释,页面同时给出运行命令、验证方法和可调整参数。

2. 构建与运行

cmake --build build --target sttnet_http_json --parallel
./build/examples/sttnet_http_json

3. 运行结果

curl -i -X POST http://127.0.0.1:8081/json -H "Content-Type: application/json" -d '{"name":"Stephen"}'
预期:返回 201 Created 和 JSON 响应。

4. 完整源码

#include <sttnet.h>

#include <iostream>
#include <memory>
#include <string>

int main()
{
    using namespace stt::network;
    using stt::system::ServerSetting;

    // 在 STTNet 创建 Reactor 或 Worker 线程前屏蔽 SIGINT/SIGTERM。
    // 这样主线程稍后能在正常 C++ 上下文中清理资源。
    if(!ServerSetting::blockTerminationSignals())
    {
        std::cerr << "failed to block termination signals\n";
        return 1;
    }

    HttpServer server;

    // GET /inspect 展示如何读取 STTNet 已解析的 HTTP 请求。
    server.setFunction("/inspect",
        [](HttpServerFDHandler &client, HttpRequestInformation &request) {
            // headerValue() 不区分大小写,并返回非拥有视图。
            // 跨回调保存时复制为拥有数据的字符串。
            const std::string contentType(request.headerValue("content-type"));

            // bodyView() 同时支持 Content-Length 和 chunked 请求体。
            const std::string body(request.bodyView());

            Json::Value response;
            response["ok"] = true;
            response["method"] = request.type;
            response["path"] = request.loc;
            response["query"] = request.para;
            response["content_type"] = contentType;
            response["body"] = body;

            // 发送成功表示响应已进入 STTNet 的
            // 有界发送队列;发送失败时返回 -2 请求关闭连接。
            return client.sendJson(response) ? 1 : -2;
        });

    // POST /json 解析客户端 JSON 请求体并校验字段。
    server.setFunction("/json",
        [](HttpServerFDHandler &client, HttpRequestInformation &request) {
            if(request.type != "POST")
            {
                Json::Value error;
                error["ok"] = false;
                error["error"] = "POST required";
                return client.sendJson(error, "405 Method Not Allowed",
                    "Allow: POST\r\n") ? 1 : -2;
            }

            // JsonCpp 的 CharReader 会返回语法错误而不是抛异常。
            const std::string body(request.bodyView());
            Json::CharReaderBuilder builder;
            std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
            Json::Value input;
            std::string parseError;

            if(!reader->parse(body.data(), body.data() + body.size(),
                              &input, &parseError))
            {
                Json::Value error;
                error["ok"] = false;
                error["error"] = "invalid JSON";
                error["detail"] = parseError;
                return client.sendJson(error, "400 Bad Request") ? 1 : -2;
            }

            if(!input.isMember("name") || !input["name"].isString() ||
               input["name"].asString().empty())
            {
                Json::Value error;
                error["ok"] = false;
                error["error"] = "field 'name' must be a non-empty string";
                return client.sendJson(error, "422 Unprocessable Entity") ? 1 : -2;
            }

            Json::Value response;
            response["ok"] = true;
            response["message"] = "hello, " + input["name"].asString();
            return client.sendJson(response, "201 Created") ? 1 : -2;
        });

    // 全局回调负责处理没有注册路由的路径。
    server.setGlobalSolveFunction(
        [](HttpServerFDHandler &client, HttpRequestInformation &) {
            Json::Value error;
            error["ok"] = false;
            error["error"] = "route not found";
            return client.sendJson(error, "404 Not Found") ? 1 : -2;
        });

    if(!server.startListen(8081))
    {
        std::cerr << "failed to listen on port 8081\n";
        return 2;
    }

    std::cout << "HTTP/JSON demo listening on http://127.0.0.1:8081\n";
    ServerSetting::waitForTerminationSignal();
    return server.close() ? 0 : 3;
}