Mongoose 嵌入式 REST API 服务器实战以 webui-rest 教程构建 JSON 接口与静态文件服务【免费下载链接】mongooseEmbedded web server, with TCP/IP network stack, MQTT and Websocket项目地址: https://gitcode.com/gh_mirrors/mon/mongoose本篇技术指南围绕 Mongoose 官方示例 tutorials/webui/webui-rest 展开讲解如何在一个不到 50 行的 C 程序里基于事件驱动模型同时实现 REST JSON 接口/api/f1、/api/sum与静态文件服务并通过浏览器端fetch调用前后端联动。读完本文你将掌握 Mongoose 的 HTTP 事件处理、URI 路由匹配、JSON 请求体解析与 JSON 响应构造的完整套路并能直接照搬到自己的嵌入式 Web 项目中。一、教程概览一个最小的 REST 服务器长什么样Mongoose 是专为嵌入式设备设计的 Web 服务器内置 TCP/IP 协议栈、MQTT 与 WebSocket 支持。webui-rest 示例是理解用 Mongoose 写 REST API的最小完整范例其代码全部集中在 main.c 中只依赖一个头文件mongoose.h仓库根目录下的 mongoose.c / mongoose.h 即当前版本源码。从源码注释可以归纳出本示例实现的三个行为main.cGET /api/f1—— 返回一个固定的模拟 JSON 结果POST /api/sum—— 从 JSON 请求体解析两个数字返回它们的和其他任意 URI —— 从web_root目录服务静态文件。所有响应均为 JSON 字符串。示例配套的 web_root/index.html 是一个纯前端页面通过浏览器原生fetch调用上述两个接口并在页面上打印调用日志构成了一个可交互的演示闭环。二、整体架构事件驱动与主循环Mongoose 采用经典的事件管理器 回调模型。示例的main()函数main.c只有四个步骤int main(void) { struct mg_mgr mgr; // Event manager mg_log_set(MG_LL_DEBUG); // Set log level mg_mgr_init(mgr); // Initialise event manager mg_http_listen(mgr, s_http_addr, fn, NULL); // Create HTTP listener for (;;) mg_mgr_poll(mgr, 1000); // Infinite event loop mg_mgr_free(mgr); return 0; }mg_log_set(MG_LL_DEBUG)把日志级别设为调试级便于观察请求处理过程mg_mgr_init()初始化事件管理器mg_http_listen()监听http://localhost:8000并把所有 HTTP 事件交给回调fn处理。该函数的实现位于 src/http.cfor (;;) mg_mgr_poll(mgr, 1000)无限轮询每次最多阻塞 1000 ms驱动整个事件循环。这是嵌入式 Web 服务器的典型形态——没有线程所有并发都靠事件驱动。回调函数fn(struct mg_connection *c, int ev, void *ev_data)main.c是真正的业务逻辑所在它只关心MG_EV_HTTP_MSG事件——即收到一个完整 HTTP 请求的消息。ev_data被强转为struct mg_http_message *hm其中hm-uri是请求的 URIhm-body是请求体。三、路由分发用 mg_match 做 URI 匹配fn内部用mg_match对 URI 做前缀匹配来分发路由main.cif (mg_match(hm-uri, mg_str(/api/f1), NULL)) { // ... respond to /api/f1 } else if (mg_match(hm-uri, mg_str(/api/sum), NULL)) { // ... respond to /api/sum } else { // ... serve static files }注意这里匹配用的是精确匹配/api/f1、/api/sum没有通配符。如果要做更灵活的路由mg_match支持两种通配*匹配到下一个斜杠为止#匹配到字符串末尾。这一点可由 test/unit_test.c 的单元测试印证ASSERT(mg_match(mg_str_n(/api/foo, 8), mg_str_n(/api/*, 6), NULL) 1); ASSERT(mg_match(mg_str_n(/api/log/static, 15), mg_str_n(/api/*, 6), NULL) 0); ASSERT(mg_match(mg_str_n(/api/log/static, 15), mg_str_n(/api/#, 6), NULL) 1);即/api/*能匹配/api/foo但不能跨斜杠匹配/api/log/static若要匹配多级路径则需用/api/#。路由匹配失败时落入else分支交给静态文件服务处理——这种API 优先、其余走静态文件的组织方式是嵌入式设备 Web UI 的常见布局。四、端点一/api/f1 与 mg_http_reply 构造 JSON 响应/api/f1是最简单的模拟数据端点main.cif (mg_match(hm-uri, mg_str(/api/f1), NULL)) { mg_http_reply(c, 200, Content-Type: application/json\r\n, {%m:%d}\n, MG_ESC(result), 123); }关键 API 是mg_http_reply(c, code, headers, fmt, ...)其实现位于 src/http.c先写出状态行HTTP/1.1 200 OK状态文本由mg_http_status_code_str自动生成、自定义响应头Content-Type: application/json以及一个预留 10 字节空位的Content-Length:字段用mg_vxprintf按fmt格式化并写入响应体最后回填真实的Content-Length长度。格式化串里的%m是 Mongoose 的 printf 扩展配合MG_ESC(result)输出一个带引号转义的 JSON 字符串键名%d输出整数 123。因此实际响应体是{result:123}浏览器端用fetch(/api/f1).then(r r.json())即可拿到该对象并读取r.result见 index.html。五、端点二/api/sum 与 mg_json_get_num 解析请求体/api/sum演示了读取 JSON 请求体 → 计算 → 回 JSON的完整链路main.c} else if (mg_match(hm-uri, mg_str(/api/sum), NULL)) { struct mg_str json hm-body; double num1, num2; if (mg_json_get_num(json, $[0], num1) mg_json_get_num(json, $[1], num2)) { mg_http_reply(c, 200, Content-Type: application/json\r\n, {%m:%g}\n, MG_ESC(result), num1 num2); } }前端约定以 JSON 数组形式提交两个加数index.htmlfetch(/api/sum, {method: POST, body: JSON.stringify([a, b])}) .then(r r.json())即请求体形如[1,2]服务端用mg_json_get_num(json, $[0], num1)提取数组第 0 个元素、$[1]提取第 1 个元素。mg_json_get_num的实现位于 src/json.c它先用mg_json_get按 JSONPath 定位 token再校验首字符必须是-或数字最后用mg_atod转成double并写入传出参数成功返回true。值得注意的两个细节容错设计只有当两个数字都成功解析时才会回复200否则函数直接返回、不产生任何响应客户端会因超时或连接关闭感知失败。读者可自行补充else分支返回400 Bad Request等错误码使接口语义更完整响应格式%g输出浮点数因此POST [1,2]会得到{result:3}与页面日志中打印的{result:3}一致。mg_json_get_num的边界行为在 test/unit_test.c 有系统覆盖例如路径不存在返回false、支持指数表示法如1e10、数组下标定位等说明该 API 可直接信任用于生产级解析。六、静态文件服务mg_http_serve_dir 兜底所有未命中 API 的请求由mg_http_serve_dir处理main.c} else { struct mg_http_serve_opts opts {.root_dir s_root_dir}; mg_http_serve_dir(c, hm, opts); }其中s_root_dir web_rootmain.c。mg_http_serve_dir声明与实现见 src/http.c 附近会根据hm-uri在根目录下查找对应文件访问/自动映射到index.html其余路径按文件名解析同时自动附带正确的 MIME 类型、Content-Length、ETag见 src/http.c 的 etag 生成等标准 HTTP 头。因此只需要一个web_root/index.html整个演示页面就能直接通过http://localhost:8000/访问。七、构建与运行示例使用统一风格的 Makefile 构建PROG ? example # Program we are building SOURCES main.c mongoose.c # Source code files CFLAGS -W -Wall -Wextra -g -I. # Build options CFLAGS_MONGOOSE -DMG_ENABLE_LINES运行方式与注意事项构建并运行makeMakefile 中all目标会先编译再直接执行程序。程序监听http://localhost:8000地址由 main.c 的s_http_addr定义可改端口Windows 支持Makefile 检测到Windows_NT时自动切换到 MinGW 的gcc并链接ws2_32库Makefile编译宏MG_ENABLE_LINES使日志/断言中携带源文件行号便于调试定位Mongoose 更多构建选项可参考mongoose.h中的说明前提本示例运行于桌面环境Unix/Windows直接编译 mongoose.c 即可使用内置网络栈若要在 STM32、ESP32 等 MCU 上运行需按 test 与 tutorials 中对应平台的移植方式接入网络驱动验证方式打开http://localhost:8000/点击页面上的Call F1与Call SUM(1, 2)按钮可在操作日志区看到两次 fetch 调用的返回结果也可用命令行直接测试curl http://localhost:8000/api/f1与curl -d [1,2] http://localhost:8000/api/sum。八、延伸从示例走向真实项目webui-rest 示例虽然短小却勾勒出了 Mongoose 设备端 Web UI 的标准骨架可直接在此基础上扩展路由拆分把fn中的if / else if链替换为注册表或mg_match通配模式/api/#支持更复杂的 REST 资源请求校验为/api/sum补充失败分支如mg_http_reply(c, 400, ..., {\error\:\bad json\})形成规范错误响应更多 JSON 能力mg_json_get_bool、mg_json_unescape、mg_json_get_tok等 API 同属 src/json.c可覆盖布尔、字符串与原始 token 的提取参考更完整的仪表盘仓库 tutorials/device-dashboard 提供 array/full/minimal 三档进阶示例展示了在同一事件回调中组合 REST 与 WebSocket 推送的真实仪表盘实现服务端逻辑示例tutorials/http/http-restful-server 给出了更完整的 RESTful 服务器与鉴权配套。总而言之webui-rest 用最少的代码演示了 Mongoose 处理 REST API 的全部关键环节——事件驱动、URI 匹配、JSON 双向编解码与静态文件兜底。理解这份示例就等于拿到了在任意嵌入式平台上快速搭建 HTTP JSON 服务的通用钥匙。【免费下载链接】mongooseEmbedded web server, with TCP/IP network stack, MQTT and Websocket项目地址: https://gitcode.com/gh_mirrors/mon/mongoose创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考