为什么用 qi-web
qi-web 是用奇语(Qi)自己写的 Web 框架,使用体验对标 Express / Go Fiber,而热路径(请求解析、路由匹配、响应序列化)下沉到 Rust 加速,实测约 122k RPS。你写的是清爽的中文路由代码,跑的是接近原生的性能。
- 路由像 Express:
获取(应用值, "/", 处理器)、提交(...)一一对应 GET / POST。 - handler 签名固定:
函数(上下文): 响应,参数提取、响应构造都有现成助手。 - 中间件是一等公民:日志、CORS、限流、鉴权内置,自定义中间件也只是一个普通函数。
第一个服务(最小可跑)
一个 qi-web 程序的骨架只有四步:创建应用 → 配置监听地址 → 注册路由 → 运行。注意每个注册函数都返回一个新的应用,必须把返回值回写到变量里。
包 主程序;
// 从 Web 包按需导入:逐个列出要用的符号
导入 Web::{ 应用, 上下文, 响应, 创建应用, 配置, 获取, 文本, 运行应用 };
// handler 签名固定为 函数(上下文): 响应
函数 主页处理(上下文值: 上下文) : 响应 {
返回 文本("你好,这里是 qi-web");
}
函数 入口() {
变量 应用值: 应用 = 创建应用();
应用值 = 配置(应用值, "127.0.0.1", 43510); // 用随机高位端口
应用值 = 获取(应用值, "/", 主页处理); // 必须回写返回值
运行应用(应用值); // 阻塞,接受连接
}
43510 / 6759),不要用 8080 / 3000 / 8000。把主机和端口提取成常量,避免多处硬编码。路由与参数
路由方法名是中文:获取(GET)、提交(POST)、整体更新(PUT)、部分更新(PATCH)、删除(DELETE)。路径参数写成 {名称},查询参数用 查询参数 / 查询参数整数 取出。
/api/users/{id}),不要在路径里写中文,否则会崩溃。但响应体和 JSON 内容可以是任意中文。导入 标准库.字符串;
// 查询参数:/api/search?q=关键字&page=2
// 查询参数 取字符串;查询参数整数 取整数并带默认值
函数 搜索(上下文值: 上下文) : 响应 {
变量 关键字: 字符串 = 查询参数(上下文值, "q");
变量 页码: 整数 = 查询参数整数(上下文值, "page", 1);
如果 (字符串::字节长度(关键字) == 0) {
返回 文本("请带上查询参数 ?q=...");
}
返回 JSON("{\"关键字\":\"" + 关键字 + "\",\"页码\":" + 整数转字符串(页码) + "}");
}
路由组让你给一批路由加统一前缀,避免重复写 /api:
变量 接口组: 路由组 = 创建路由组("/api");
应用值 = 组获取(应用值, 接口组, "/search", 搜索); // → GET /api/search
JSON 响应
响应助手覆盖常见场景:文本(...) 纯文本、HTML(...)、JSON(...)、重定向(...)。handler 必须返回一个 响应,不能返回裸字符串。读取 POST 请求体用 请求主体。
// POST 路由:读取请求体并回显成 JSON
函数 创建用户(上下文值: 上下文) : 响应 {
变量 主体: 字符串 = 请求主体(上下文值);
如果 (字符串::字节长度(主体) == 0) {
返回 文本("请在请求体里提交数据");
}
返回 JSON("{\"状态\":\"已创建\",\"收到\":\"" + 主体 + "\"}");
}
中间件
内置中间件一行挂载:使用日志(应用值) 打请求日志、使用跨域(应用值) 开 CORS。自定义中间件就是一个函数,签名为 函数(下一步, 上下文): 响应——你在调用 下一步(上下文) 前后做事情,比如计时、补响应头、鉴权,甚至提前返回。
导入 标准库.时间;
// 先调用 下一步(上下文) 拿到响应,再给响应加一个头
函数 计时中间件(下一步: 函数(上下文): 响应, 上下文值: 上下文) : 响应 {
变量 开始毫秒: 整数 = 时间.当前毫秒();
变量 响应值: 响应 = 下一步(上下文值);
变量 耗时: 整数 = 时间.当前毫秒() - 开始毫秒;
返回 设置响应头(响应值, "X-耗时毫秒", 整数转字符串(耗时));
}
// 在 入口() 里挂载(注意顺序与回写)
应用值 = 使用日志(应用值);
应用值 = 使用跨域(应用值);
应用值 = 使用中间件(应用值, 计时中间件);
首页、获取 这类框架已经导出的名字,否则链接期会因符号撞名而失败。给自己的 handler 起一个不冲突的名字,比如 主页处理。运行与验证
把上面这些拼到一个文件里(仓库里就是 qi-web/examples/教程_快速开始.qi),用 qi 编译器运行:
$ qi run qi-web/examples/教程_快速开始.qi
服务已启动,监听 http://127.0.0.1:43510
启动服务器: http://127.0.0.1:43510
服务器运行中(并发处理),按 Ctrl+C 优雅关闭
另开一个终端,用 curl 真实打几个请求:
$ curl -i http://127.0.0.1:43510/
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
X-耗时毫秒: 0
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 44
你好,这里是 qi-web 快速开始教程
$ curl "http://127.0.0.1:43510/api/search?q=qi&page=2"
{"关键字":"qi","页码":2}
$ curl -X POST -d 'x=1' http://127.0.0.1:43510/api/users
{"状态":"已创建","收到":"x=1"}
注意第一个响应里多出来的 X-耗时毫秒 头——那正是我们的计时中间件加上去的,Access-Control-Allow-Origin: * 则来自 使用跨域。同时服务端会打出请求日志:
[请求] GET /
[响应] 200
[请求] GET /api/search
[响应] 200
[请求] POST /api/users
[响应] 200
下一步
你已经能写出一个带路由、参数、JSON 和中间件的 qi-web 服务了。继续深入:
- 不熟悉奇语语法?先读 奇语语言快速开始。
- 想写命令行工具?看 qi-cli 快速开始,Cobra 风格的命令树。
- 想用奇语搭 LLM Agent?看 qi-harness 快速开始,可观测、可重试的工具调用循环。